mirror of
https://github.com/tribes2/engine.git
synced 2026-01-22 04:34:45 +00:00
527 lines
16 KiB
C++
527 lines
16 KiB
C++
//-----------------------------------------------------------------------------
|
|
// V12 Engine
|
|
//
|
|
// Copyright (c) 2001 GarageGames.Com
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _NETCONNECTION_H_
|
|
#define _NETCONNECTION_H_
|
|
|
|
#ifndef _MPOINT_H_
|
|
#include "math/mPoint.h"
|
|
#endif
|
|
#ifndef _NETOBJECT_H_
|
|
#include "sim/netObject.h"
|
|
#endif
|
|
#ifndef _NETSTRINGTABLE_H_
|
|
#include "sim/netStringTable.h"
|
|
#endif
|
|
#ifndef _EVENT_H_
|
|
#include "platform/event.h"
|
|
#endif
|
|
#ifndef _DNET_H_
|
|
#include "core/dnet.h"
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
// the sim connection encapsulates the packet stream,
|
|
// ghost manager, event manager and playerPSC of the old tribes net stuff
|
|
|
|
class NetConnection;
|
|
class NetObject;
|
|
class BitStream;
|
|
class ResizeBitStream;
|
|
class Stream;
|
|
class Point3F;
|
|
|
|
struct GhostInfo;
|
|
struct SubPacketRef; // defined in NetConnection subclass
|
|
|
|
//#define DEBUG_NET
|
|
|
|
#ifdef DEBUG_NET
|
|
#define DEBUG_LOG(x) if(mLogging){Con::printf x;}
|
|
#else
|
|
#define DEBUG_LOG(x)
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class NetEvent : public ConsoleObject
|
|
{
|
|
// event manager variables
|
|
public:
|
|
typedef ConsoleObject Parent;
|
|
S32 mSeqCount;
|
|
NetEvent *mNextEvent;
|
|
bool mIsPosted;
|
|
enum {
|
|
GuaranteedOrdered = 0,
|
|
Guaranteed = 1,
|
|
Unguaranteed = 2
|
|
} mGuaranteeType;
|
|
|
|
NetEvent() { mGuaranteeType = GuaranteedOrdered; mIsPosted = false; }
|
|
virtual ~NetEvent();
|
|
|
|
NetConnectionId mSourceId;
|
|
#ifdef DEBUG_NET
|
|
virtual const char *getDebugName();
|
|
#endif
|
|
virtual void write(NetConnection *ps, BitStream *bstream) = 0;
|
|
virtual void pack(NetConnection *ps, BitStream *bstream) = 0;
|
|
virtual void unpack(NetConnection *ps, BitStream *bstream) = 0;
|
|
virtual void process(NetConnection *ps) = 0;
|
|
virtual void notifySent(NetConnection *ps);
|
|
virtual void notifyDelivered(NetConnection *ps, bool madeit);
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class NetConnection : public ConnectionProtocol, public SimGroup
|
|
{
|
|
typedef SimGroup Parent;
|
|
|
|
public:
|
|
struct GhostRef
|
|
{
|
|
U32 mask;
|
|
U32 ghostInfoFlags;
|
|
GhostInfo *ghost;
|
|
GhostRef *nextRef;
|
|
GhostRef *nextUpdateChain;
|
|
};
|
|
enum {
|
|
HashTableSize = 127,
|
|
};
|
|
public:
|
|
struct PacketNotify
|
|
{
|
|
// packet stream notify stuff:
|
|
bool rateChanged;
|
|
bool maxRateChanged;
|
|
U32 sendTime;
|
|
|
|
NetEvent *eventList;
|
|
GhostRef *ghostList;
|
|
SubPacketRef *subList; // defined by subclass - used as desired
|
|
|
|
PacketNotify *nextPacket;
|
|
PacketNotify();
|
|
};
|
|
virtual PacketNotify *allocNotify();
|
|
//----------------------------------------------------------------
|
|
// Connection functions
|
|
//----------------------------------------------------------------
|
|
|
|
private:
|
|
U32 mLastUpdateTime;
|
|
F32 mRoundTripTime;
|
|
F32 mPacketLoss;
|
|
U32 mProtocolVersion;
|
|
|
|
U32 mSimulatedPing;
|
|
F32 mSimulatedPacketLoss;
|
|
|
|
U32 mClientConnectSequence;
|
|
U32 mServerConnectSequence;
|
|
|
|
struct NetRate
|
|
{
|
|
U32 updateDelay;
|
|
S32 packetSize;
|
|
bool changed;
|
|
};
|
|
|
|
NetRate mCurRate;
|
|
NetRate mMaxRate;
|
|
|
|
PacketNotify *mNotifyQueueHead;
|
|
PacketNotify *mNotifyQueueTail;
|
|
|
|
SimObjectId mConnectionObjectId;
|
|
|
|
bool mMissionPathsSent;
|
|
|
|
NetAddress mNetAddress;
|
|
|
|
// timeout management stuff:
|
|
U32 mPingSendCount;
|
|
U32 mPingRetryCount;
|
|
U32 mLastPingSendTime;
|
|
|
|
NetConnection *mNextTableHash;
|
|
static NetConnection *mHashTable[HashTableSize];
|
|
bool mIsNetworkConnection;
|
|
protected:
|
|
static NetConnection *mServerConnection;
|
|
static NetConnection *mLocalClientConnection;
|
|
static char mErrorBuffer[256];
|
|
|
|
public:
|
|
static char *getErrorBuffer() { return mErrorBuffer; }
|
|
#ifdef DEBUG_NET
|
|
bool mLogging;
|
|
void setLogging(bool logging) { mLogging = logging; }
|
|
#endif
|
|
void setSimulatedNetParams(F32 packetLoss, U32 ping)
|
|
{ mSimulatedPacketLoss = packetLoss; mSimulatedPing = ping; }
|
|
bool isServerConnection() { return this == mServerConnection; }
|
|
bool isLocalConnection() { return mConnectionObjectId != 0; }
|
|
bool isNetworkConnection() { return mIsNetworkConnection; }
|
|
void setNetworkConnection(bool net) { mIsNetworkConnection = net; }
|
|
// call this if the "connection" is local to this app
|
|
// short-circuits protocol layer
|
|
|
|
void setRemoteConnectionObjectId(SimObjectId objectId);
|
|
SimObjectId getRemoteConnectionObjectId() { return mConnectionObjectId; }
|
|
void setSequences(U32 ccs, U32 css);
|
|
void getSequences(U32 *ccs, U32 *css);
|
|
|
|
void setProtocolVersion(U32 protocol)
|
|
{ mProtocolVersion = protocol; }
|
|
U32 getProtocolVersion()
|
|
{ return mProtocolVersion; }
|
|
F32 getRoundTripTime()
|
|
{ return mRoundTripTime; }
|
|
F32 getPacketLoss()
|
|
{ return( mPacketLoss ); }
|
|
|
|
static void setLastError(const char *fmt,...);
|
|
|
|
static void setServerConnection(NetConnection *conn);
|
|
static void setLocalClientConnection(NetConnection *conn);
|
|
|
|
static NetConnection *getServerConnection() { return mServerConnection; }
|
|
static NetConnection *getLocalClientConnection() { return mLocalClientConnection; }
|
|
|
|
void checkMaxRate();
|
|
|
|
void handlePacket(BitStream *stream);
|
|
void processRawPacket(BitStream *stream);
|
|
void handleNotify(bool recvd);
|
|
void handleConnectionEstablished();
|
|
void keepAlive();
|
|
|
|
const NetAddress *getNetAddress();
|
|
void setNetAddress(const NetAddress *address);
|
|
Net::Error sendPacket(BitStream *stream);
|
|
private:
|
|
void netAddressTableInsert();
|
|
void netAddressTableRemove();
|
|
public:
|
|
static NetConnection *lookup(const NetAddress *remoteAddress);
|
|
|
|
bool checkTimeout(U32 time); // returns true if the connection timed out
|
|
|
|
void checkPacketSend();
|
|
|
|
bool missionPathsSent() const { return mMissionPathsSent; }
|
|
void setMissionPathsSent(const bool s) { mMissionPathsSent = s; }
|
|
|
|
static void consoleInit();
|
|
|
|
bool onAdd();
|
|
void onRemove();
|
|
|
|
NetConnection(bool ghostFrom = false, bool ghostTo = false, bool sendEvents = false);
|
|
~NetConnection();
|
|
|
|
DECLARE_CONOBJECT(NetConnection);
|
|
protected:
|
|
virtual void readPacket(BitStream *bstream);
|
|
virtual void writePacket(BitStream *bstream, PacketNotify *note);
|
|
virtual void packetReceived(PacketNotify *note);
|
|
virtual void packetDropped(PacketNotify *note);
|
|
virtual void connectionError(const char *errorString);
|
|
|
|
//----------------------------------------------------------------
|
|
// event manager functions/code:
|
|
//----------------------------------------------------------------
|
|
|
|
private:
|
|
NetEvent *mSendEventQueueHead;
|
|
NetEvent *mSendEventQueueTail;
|
|
NetEvent *mUnorderedSendEventQueueHead;
|
|
NetEvent *mUnorderedSendEventQueueTail;
|
|
NetEvent *mWaitSeqEvents;
|
|
NetEvent *mNotifyEventList;
|
|
bool mSendingEvents;
|
|
|
|
S32 mNextSendEventSeq;
|
|
S32 mNextRecvEventSeq;
|
|
S32 mLastAckedEventSeq;
|
|
|
|
enum {
|
|
InvalidSendEventSeq = -1,
|
|
FirstValidSendEventSeq = 0
|
|
};
|
|
|
|
void eventPacketDropped(PacketNotify *notify);
|
|
void eventPacketReceived(PacketNotify *notify);
|
|
|
|
void eventWritePacket(BitStream *bstream, PacketNotify *notify);
|
|
void eventReadPacket(BitStream *bstream);
|
|
|
|
void eventWriteStartBlock(ResizeBitStream *stream);
|
|
void eventReadStartBlock(BitStream *stream);
|
|
public:
|
|
bool postNetEvent(NetEvent *event);
|
|
|
|
//----------------------------------------------------------------
|
|
// String table functions
|
|
//----------------------------------------------------------------
|
|
|
|
private:
|
|
|
|
U16 mStringXLTable[NetStringTable::MaxStrings];
|
|
U8 mStringSentBitArray[NetStringTable::MaxStrings >> 3];
|
|
NetConnection *mNextConnection;
|
|
NetConnection *mPrevConnection;
|
|
static NetConnection *mConnectionList;
|
|
public:
|
|
static NetConnection *getConnectionList() { return mConnectionList; }
|
|
NetConnection *getNext() { return mNextConnection; }
|
|
|
|
void mapString(U32 remoteId, U32 localId);
|
|
void clearString(U32 id);
|
|
void checkString(U32 id);
|
|
U32 translateRemoteStringId(U32 id) { return mStringXLTable[id]; }
|
|
void validateSendString(const char *str);
|
|
void packString(BitStream *stream, const char *str);
|
|
void unpackString(BitStream *stream, char readBuffer[1024]);
|
|
|
|
//----------------------------------------------------------------
|
|
// ghost manager functions/code:
|
|
//----------------------------------------------------------------
|
|
|
|
protected:
|
|
enum
|
|
{
|
|
GhostAlwaysDone,
|
|
ReadyForNormalGhosts,
|
|
EndGhosting,
|
|
GhostAlwaysStarting,
|
|
};
|
|
GhostInfo **mGhostArray; // linked list of ghostInfos ghosted by this side of the connection
|
|
|
|
U32 mGhostZeroUpdateIndex; // index in mGhostArray of first ghost with 0 update mask
|
|
U32 mGhostFreeIndex; // index in mGhostArray of first free ghost
|
|
|
|
bool mGhostFrom; // can this side ghost objects over to the other?
|
|
bool mGhostTo; // can this side accept remote ghosts?
|
|
bool mGhosting; // am I currently ghosting objects over?
|
|
bool mScoping; // am I currently allowing objects to be scoped?
|
|
U32 mGhostingSequence; // sequence number describing this ghosting session
|
|
|
|
// mLocalGhosts pointer is NULL if mGhostTo is false
|
|
NetObject **mLocalGhosts; // local ghost for remote object
|
|
|
|
// these pointers are NULL if mGhostFrom is false
|
|
GhostInfo *mGhostRefs; // allocated array of ghostInfos
|
|
GhostInfo **mGhostLookupTable; // table indexed by object id->GhostInfo
|
|
|
|
SimObjectPtr<NetObject> mScopeObject;
|
|
|
|
void clearGhostInfo();
|
|
bool validateGhostArray();
|
|
|
|
void ghostPacketDropped(PacketNotify *notify);
|
|
void ghostPacketReceived(PacketNotify *notify);
|
|
|
|
void ghostWritePacket(BitStream *bstream, PacketNotify *notify);
|
|
void ghostReadPacket(BitStream *bstream);
|
|
void freeGhostInfo(GhostInfo *);
|
|
|
|
void ghostWriteStartBlock(ResizeBitStream *stream);
|
|
void ghostReadStartBlock(BitStream *stream);
|
|
|
|
public:
|
|
enum { MaxGhostCount = 1024, GhostIdBitSize = 10, GhostLookupTableSize = 1024 };
|
|
|
|
virtual void doneScopingScene() {}
|
|
void setScopeObject(NetObject *object);
|
|
NetObject *getScopeObject();
|
|
|
|
void objectInScope(NetObject *object);
|
|
void objectLocalScopeAlways(NetObject *object);
|
|
void objectLocalClearAlways(NetObject *object);
|
|
|
|
NetObject *resolveGhost(S32 id);
|
|
NetObject *resolveGhostParent(S32 id);
|
|
void ghostPushNonZero(GhostInfo *gi);
|
|
void ghostPushToZero(GhostInfo *gi);
|
|
void ghostPushZeroToFree(GhostInfo *gi);
|
|
inline void ghostPushFreeToZero(GhostInfo *info);
|
|
|
|
S32 getGhostIndex(NetObject *object);
|
|
|
|
void resetGhosting();
|
|
void activateGhosting();
|
|
bool isGhosting() { return mGhosting; }
|
|
|
|
void detachObject(GhostInfo *info);
|
|
virtual void handleGhostMessage(S32 message, U32 sequence, U32 ghostCount);
|
|
void setGhostAlwaysObject(NetObject *object, U32 index);
|
|
|
|
//----------------------------------------------------------------
|
|
// Demo recording functions
|
|
//----------------------------------------------------------------
|
|
|
|
private:
|
|
Stream *mDemoWriteStream;
|
|
Stream *mDemoReadStream;
|
|
U32 mDemoWriteStartTime;
|
|
U32 mDemoReadStartTime;
|
|
U32 mDemoLastWriteTime;
|
|
|
|
U32 mDemoRealStartTime;
|
|
|
|
public:
|
|
enum {
|
|
BlockTypePacket,
|
|
NetConnectionBlockTypeCount
|
|
};
|
|
bool isRecording()
|
|
{ return mDemoWriteStream != NULL; }
|
|
bool isPlayingBack()
|
|
{ return mDemoReadStream != NULL; }
|
|
|
|
void recordBlock(U32 time, U32 type, U32 size, void *data);
|
|
virtual void handleRecordedBlock(U32 type, U32 size, void *data);
|
|
void readNextDemoBlock();
|
|
|
|
bool startDemoRecord(const char *fileName);
|
|
bool replayDemoRecord(const char *fileName);
|
|
void startDemoRead();
|
|
void stopRecording();
|
|
|
|
virtual void writeDemoStartBlock(ResizeBitStream *stream);
|
|
virtual void readDemoStartBlock(BitStream *stream);
|
|
virtual U32 getDemoTickSize();
|
|
virtual void demoPlaybackComplete();
|
|
|
|
|
|
//----------------------------------------------------------------
|
|
// Connection relative compression
|
|
//----------------------------------------------------------------
|
|
|
|
private:
|
|
bool mCompressRelative;
|
|
Point3F mCompressPoint;
|
|
|
|
public:
|
|
void clearCompression();
|
|
void setCompressionPoint(const Point3F& p);
|
|
|
|
// Matching calls to these compression methods must, of course,
|
|
// have matching scale values.
|
|
virtual void writeCompressed(BitStream* stream,const Point3F& p,F32 scale = 0.01f);
|
|
virtual void readCompressed(BitStream* stream,Point3F* p,F32 scale = 0.01f);
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
struct GhostInfo
|
|
{
|
|
public: // required for MSVC
|
|
|
|
// NOTE:
|
|
// if the size of this structure changes, the
|
|
// NetConnection::getGhostIndex function MUST be changed
|
|
// to reflect.
|
|
|
|
NetObject *obj; // the real object
|
|
U32 updateMask; // 32 bits of object info
|
|
NetConnection::GhostRef *updateChain; // chain of updates for this object in packets
|
|
GhostInfo *nextObjectRef; // next ghost ref for this object (another connection)
|
|
|
|
GhostInfo *prevObjectRef; // prev ghost ref for this object
|
|
NetConnection *connection;
|
|
GhostInfo *nextLookupInfo;
|
|
U32 updateSkipCount;
|
|
|
|
U32 flags;
|
|
F32 priority;
|
|
U32 index;
|
|
U32 arrayIndex;
|
|
|
|
enum Flags
|
|
{
|
|
Valid = BIT(0),
|
|
InScope = BIT(1),
|
|
ScopeAlways = BIT(2),
|
|
NotYetGhosted = BIT(3),
|
|
Ghosting = BIT(4),
|
|
KillGhost = BIT(5),
|
|
KillingGhost = BIT(6),
|
|
ScopedEvent = BIT(7),
|
|
ScopeLocalAlways = BIT(8),
|
|
};
|
|
};
|
|
|
|
inline void NetConnection::ghostPushNonZero(GhostInfo *info)
|
|
{
|
|
AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex.");
|
|
AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object.");
|
|
if(info->arrayIndex != mGhostZeroUpdateIndex)
|
|
{
|
|
mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex;
|
|
mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex];
|
|
mGhostArray[mGhostZeroUpdateIndex] = info;
|
|
info->arrayIndex = mGhostZeroUpdateIndex;
|
|
}
|
|
mGhostZeroUpdateIndex++;
|
|
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
|
|
}
|
|
|
|
inline void NetConnection::ghostPushToZero(GhostInfo *info)
|
|
{
|
|
AssertFatal(info->arrayIndex < mGhostZeroUpdateIndex, "Out of range arrayIndex.");
|
|
AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object.");
|
|
mGhostZeroUpdateIndex--;
|
|
if(info->arrayIndex != mGhostZeroUpdateIndex)
|
|
{
|
|
mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex;
|
|
mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex];
|
|
mGhostArray[mGhostZeroUpdateIndex] = info;
|
|
info->arrayIndex = mGhostZeroUpdateIndex;
|
|
}
|
|
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
|
|
}
|
|
|
|
inline void NetConnection::ghostPushZeroToFree(GhostInfo *info)
|
|
{
|
|
AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex.");
|
|
AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object.");
|
|
mGhostFreeIndex--;
|
|
if(info->arrayIndex != mGhostFreeIndex)
|
|
{
|
|
mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex;
|
|
mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex];
|
|
mGhostArray[mGhostFreeIndex] = info;
|
|
info->arrayIndex = mGhostFreeIndex;
|
|
}
|
|
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
|
|
}
|
|
|
|
inline void NetConnection::ghostPushFreeToZero(GhostInfo *info)
|
|
{
|
|
AssertFatal(info->arrayIndex >= mGhostFreeIndex, "Out of range arrayIndex.");
|
|
AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object.");
|
|
if(info->arrayIndex != mGhostFreeIndex)
|
|
{
|
|
mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex;
|
|
mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex];
|
|
mGhostArray[mGhostFreeIndex] = info;
|
|
info->arrayIndex = mGhostFreeIndex;
|
|
}
|
|
mGhostFreeIndex++;
|
|
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
|
|
}
|
|
|
|
#endif
|