t2 engine svn checkout

This commit is contained in:
loop 2024-01-07 04:36:33 +00:00
commit ff569bd2ae
988 changed files with 394180 additions and 0 deletions

1637
sim/actionMap.cc Normal file

File diff suppressed because it is too large Load diff

142
sim/actionMap.h Normal file
View file

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _ACTIONMAP_H_
#define _ACTIONMAP_H_
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "Core/tVector.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
struct InputEvent;
struct EventDescriptor
{
U8 flags; // Combination of any Modifiers
U8 eventType; // SI_KEY, etc.
U16 eventCode; // From event.h
};
class ActionMap : public SimObject
{
typedef SimObject Parent;
protected:
bool onAdd();
struct Node {
U32 modifiers;
U32 action;
enum Flags {
Ranged = 1 << 0,
HasScale = 1 << 1,
HasDeadZone = 1 << 2,
Inverted = 1 << 3,
BindCmd = 1 << 4
};
U32 flags;
F32 deadZoneBegin;
F32 deadZoneEnd;
F32 scaleFactor;
StringTableEntry consoleFunction;
char *makeConsoleCommand;
char *breakConsoleCommand;
};
struct DeviceMap
{
U32 deviceType;
U32 deviceInst;
Vector<Node> nodeMap;
DeviceMap() {
VECTOR_SET_ASSOCIATION(nodeMap);
}
~DeviceMap();
};
struct BreakEntry
{
U32 deviceType;
U32 deviceInst;
U32 objInst;
StringTableEntry consoleFunction;
char *breakConsoleCommand;
// It's possible that the node could be deleted (unlikely, but possible,
// so we replicate the node flags here...
//
U32 flags;
F32 deadZoneBegin;
F32 deadZoneEnd;
F32 scaleFactor;
};
Vector<DeviceMap*> mDeviceMaps;
static Vector<BreakEntry> smBreakTable;
// Find: return NULL if not found in current map, Get: create if not
// found.
const Node* findNode(const U32 inDeviceType, const U32 inDeviceInst,
const U32 inModifiers, const U32 inAction);
bool findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex );
Node* getNode(const U32 inDeviceType, const U32 inDeviceInst,
const U32 inModifiers, const U32 inAction);
void removeNode(const U32 inDeviceType, const U32 inDeviceInst,
const U32 inModifiers, const U32 inAction);
void enterBreakEvent(const InputEvent* pEvent, const Node* pNode);
static const char* getModifierString(const U32 modifiers);
public:
ActionMap();
~ActionMap();
void dumpActionMap(const char* fileName, const bool append) const;
static bool createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor);
//static void consoleInit();
bool processBind(const U32 argc, const char** argv);
bool processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd);
bool processUnbind(const char *device, const char *action);
// Console interface functions:
const char* getBinding( const char* command ); // Find what the given command is bound to
const char* getCommand( const char* device, const char* action ); // Find what command is bound to the given event descriptor
bool isInverted( const char* device, const char* action );
F32 getScale( const char* device, const char* action );
const char* getDeadZone( const char* device, const char* action );
//
static bool getKeyString(const U32 action, char* buffer);
static bool getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer);
static const char* buildActionString( const InputEvent* event );
bool processAction(const InputEvent*);
static bool checkBreakTable(const InputEvent*);
static bool handleEvent(const InputEvent*);
static bool handleEventGlobal(const InputEvent*);
static bool getDeviceTypeAndInstance(const char *device, U32 &deviceType, U32 &deviceInstance);
DECLARE_CONOBJECT(ActionMap);
};
#endif // _ACTIONMAP_H_

View file

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Sim/cannedChatDataBlock.h"
#include "console/consoleTypes.h"
//--------------------------------------------------------------------------
static const char* getDataTypeCannedChatItemPtr( void* dptr, EnumTable*, BitSet32 )
{
CannedChatItem** obj = reinterpret_cast<CannedChatItem**>( dptr );
return ( *obj ? (*obj)->getName() : "" );
}
static void setDataTypeCannedChatItemPtr( void* dptr, S32 argc, const char** argv, EnumTable*, BitSet32 )
{
if ( argc == 1 )
{
if ( !Sim::findObject( argv[0], *reinterpret_cast<CannedChatItem**>( dptr ) ) )
Con::printf( "Object \"%s\" is not a CannedChatItem data block", argv[0] );
}
else
Con::printf( "(TypeCannedChatItemPtr) Can't set multiple args to a single pointer." );
}
//--------------------------------------------------------------------------
IMPLEMENT_CO_DATABLOCK_V1( CannedChatItem );
CannedChatItem::CannedChatItem()
{
mName = NULL;
mText = NULL;
mAudioFile = NULL;
mAnimation = NULL;
mTeamOnly = false;
mPlay3D = false;
}
//--------------------------------------------------------------------------
void CannedChatItem::consoleInit()
{
addField( "name", TypeString, Offset( mName, CannedChatItem ) );
addField( "text", TypeString, Offset( mText, CannedChatItem ) );
addField( "audioFile", TypeString, Offset( mAudioFile, CannedChatItem ) );
addField( "animation", TypeString, Offset( mAnimation, CannedChatItem ) );
// more?
addField( "teamOnly", TypeBool, Offset( mTeamOnly, CannedChatItem ) );
addField( "play3D", TypeBool, Offset( mPlay3D, CannedChatItem ) );
Con::registerType( TypeCannedChatItemPtr, sizeof( CannedChatItem* ), getDataTypeCannedChatItemPtr, setDataTypeCannedChatItemPtr );
}
//--------------------------------------------------------------------------
void CannedChatItem::initPersistFields()
{
Parent::initPersistFields();
}
//--------------------------------------------------------------------------
bool CannedChatItem::onAdd()
{
if ( !Parent::onAdd() )
return false;
return true;
}

37
sim/cannedChatDataBlock.h Normal file
View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _CANNEDCHATDATABLOCK_H_
#define _CANNEDCHATDATABLOCK_H_
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
struct CannedChatItem : public SimDataBlock
{
typedef SimDataBlock Parent;
StringTableEntry mName;
StringTableEntry mText;
StringTableEntry mAudioFile;
StringTableEntry mAnimation;
bool mTeamOnly;
bool mPlay3D;
// more...
CannedChatItem();
DECLARE_CONOBJECT( CannedChatItem );
static void consoleInit();
static void initPersistFields();
virtual bool onAdd();
};
#endif // _H_CANNEDCHATDATABLOCK_

392
sim/decalManager.cc Normal file
View file

@ -0,0 +1,392 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Sim/decalManager.h"
#include "PlatformWin32/platformGL.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "ts/tsShapeInstance.h"
#include "Core/bitStream.h"
#include "console/consoleTypes.h"
#define DML_DIR "textures/special/"
bool DecalManager::smDecalsOn = true;
const U32 DecalManager::csmFreePoolBlockSize = 256;
U32 DecalManager::smMaxNumDecals = 256;
U32 DecalManager::smDecalTimeout = 5000;
DecalManager* gDecalManager = NULL;
IMPLEMENT_CONOBJECT(DecalManager);
IMPLEMENT_CO_DATABLOCK_V1(DecalData);
namespace {
int QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2)
{
const DecalInstance** pd1 = (const DecalInstance**)p1;
const DecalInstance** pd2 = (const DecalInstance**)p2;
return S32((*pd1)->decalData) - S32((*pd2)->decalData);
}
} // namespace {}
//--------------------------------------------------------------------------
DecalData::DecalData()
{
sizeX = 1;
sizeY = 1;
textureName = "";
}
DecalData::~DecalData()
{
if(gDecalManager)
gDecalManager->dataDeleted(this);
}
void DecalData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(sizeX);
stream->write(sizeY);
stream->writeString(textureName);
}
void DecalData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&sizeX);
stream->read(&sizeY);
textureName = stream->readSTString();
}
bool DecalData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if (sizeX < 0.0) {
Con::warnf("DecalData::preload: sizeX < 0");
sizeX = 0;
}
if (sizeY < 0.0) {
Con::warnf("DecalData::preload: sizeX < 0");
sizeY = 0;
}
if (textureName == NULL || textureName[0] == '\0') {
Con::errorf("No texture name for decal!");
return false;
}
if (!server) {
textureHandle = TextureHandle(textureName, MeshTexture);
if (textureHandle.getGLName() == 0) {
Con::errorf("Unable to load texture: %s for decal!", textureName);
return false;
}
}
return true;
}
IMPLEMENT_SETDATATYPE(DecalData)
IMPLEMENT_GETDATATYPE(DecalData)
void DecalData::initPersistFields()
{
Con::registerType(TypeDecalDataPtr, sizeof(DecalData*),
REF_GETDATATYPE(DecalData),
REF_SETDATATYPE(DecalData));
addField("sizeX", TypeF32, Offset(sizeX, DecalData));
addField("sizeY", TypeF32, Offset(sizeY, DecalData));
addField("textureName", TypeString, Offset(textureName, DecalData));
}
DecalManager::DecalManager()
{
mTypeMask |= DecalManagerObjectType;
mObjBox.min.set(-1e7, -1e7, -1e7);
mObjBox.max.set( 1e7, 1e7, 1e7);
mWorldBox.min.set(-1e7, -1e7, -1e7);
mWorldBox.max.set( 1e7, 1e7, 1e7);
mFreePool = NULL;
VECTOR_SET_ASSOCIATION(mDecalQueue);
VECTOR_SET_ASSOCIATION(mFreePoolBlocks);
}
DecalManager::~DecalManager()
{
mFreePool = NULL;
for (S32 i = 0; i < mFreePoolBlocks.size(); i++)
{
delete [] mFreePoolBlocks[i];
}
mDecalQueue.clear();
}
DecalInstance* DecalManager::allocateDecalInstance()
{
if (mFreePool == NULL)
{
// Allocate a new block of decals
mFreePoolBlocks.push_back(new DecalInstance[csmFreePoolBlockSize]);
// Init them onto the free pool chain
DecalInstance* pNewInstances = mFreePoolBlocks.last();
for (U32 i = 0; i < csmFreePoolBlockSize - 1; i++)
pNewInstances[i].next = &pNewInstances[i + 1];
pNewInstances[csmFreePoolBlockSize - 1].next = NULL;
mFreePool = pNewInstances;
}
AssertFatal(mFreePool != NULL, "Error, should always have a free pool available here!");
DecalInstance* pRet = mFreePool;
mFreePool = pRet->next;
pRet->next = NULL;
return pRet;
}
void DecalManager::freeDecalInstance(DecalInstance* trash)
{
AssertFatal(trash != NULL, "Error, no trash pointer to free!");
trash->next = mFreePool;
mFreePool = trash;
}
void DecalManager::dataDeleted(DecalData *data)
{
for(S32 i = mDecalQueue.size() - 1; i >= 0; i--)
{
DecalInstance *inst = mDecalQueue[i];
if(inst->decalData == data)
{
freeDecalInstance(inst);
mDecalQueue.erase(U32(i));
}
}
}
void DecalManager::consoleInit()
{
Con::addVariable("$pref::decalsOn", TypeBool, &smDecalsOn);
Con::addVariable("$pref::Decal::maxNumDecals", TypeS32, &smMaxNumDecals);
Con::addVariable("$pref::Decal::decalTimeout", TypeS32, &smDecalTimeout);
}
void DecalManager::addDecal(Point3F pos, Point3F normal, DecalData* decalData)
{
if (smMaxNumDecals == 0)
return;
// DMM: Rework this, should be based on time
if(mDecalQueue.size() >= smMaxNumDecals)
{
DecalInstance* holder = mDecalQueue.front();
mDecalQueue.pop_front();
freeDecalInstance(holder);
}
Point3F vecX, vecY;
DecalInstance* newDecal = allocateDecalInstance();
newDecal->decalData = decalData;
newDecal->allocTime = Platform::getVirtualMilliseconds();
if(mFabs(normal.z) > 0.9f)
mCross(normal, Point3F(0.0f, 1.0f, 0.0f), &vecX);
else
mCross(normal, Point3F(0.0f, 0.0f, 1.0f), &vecX);
mCross(vecX, normal, &vecY);
normal.normalizeSafe();
Point3F position = Point3F(pos.x + (normal.x * 0.008), pos.y + (normal.y * 0.008), pos.z + (normal.z * 0.008));
vecX.normalizeSafe();
vecY.normalizeSafe();
vecX *= decalData->sizeX;
vecY *= decalData->sizeY;
newDecal->point[0] = position + vecX + vecY;
newDecal->point[1] = position + vecX - vecY;
newDecal->point[2] = position - vecX - vecY;
newDecal->point[3] = position - vecX + vecY;
mDecalQueue.push_back(newDecal);
mQueueDirty = true;
}
void DecalManager::addDecal(Point3F pos, Point3F rot, Point3F normal, DecalData* decalData)
{
if(mDot(rot, normal) < 0.98)
{
// DMM: Rework this, should be based on time
if(mDecalQueue.size() >= smMaxNumDecals)
{
DecalInstance* holder = mDecalQueue.front();
mDecalQueue.pop_front();
freeDecalInstance(holder);
}
Point3F vecX, vecY;
DecalInstance* newDecal = allocateDecalInstance();
newDecal->decalData = decalData;
newDecal->allocTime = Platform::getVirtualMilliseconds();
mCross(rot, normal, &vecX);
mCross(normal, vecX, &vecY);
normal.normalize();
Point3F position = Point3F(pos.x + (normal.x * 0.008), pos.y + (normal.y * 0.008), pos.z + (normal.z * 0.008));
vecX.normalize();
vecY.normalize();
vecX *= decalData->sizeX;
vecY *= decalData->sizeY;
newDecal->point[0] = position + vecX + vecY;
newDecal->point[1] = position + vecX - vecY;
newDecal->point[2] = position - vecX - vecY;
newDecal->point[3] = position - vecX + vecY;
mDecalQueue.push_back(newDecal);
mQueueDirty = true;
}
}
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (!smDecalsOn) return false;
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
if (mDecalQueue.size() == 0)
return false;
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::BeginSort;
state->insertRenderImage(image);
U32 currMs = Platform::getVirtualMilliseconds();
for (S32 i = mDecalQueue.size() - 1; i >= 0; i--)
{
U32 age = currMs - mDecalQueue[i]->allocTime;
if (age > smDecalTimeout)
{
freeDecalInstance(mDecalQueue[i]);
mDecalQueue.erase(i);
}
else if (age > ((3 * smDecalTimeout) / 4))
{
mDecalQueue[i]->fade = 1.0f - (F32(age - ((3 * smDecalTimeout) / 4)) / F32(smDecalTimeout / 4));
}
else
{
mDecalQueue[i]->fade = 1.0f;
}
}
if (mQueueDirty == true)
{
// Sort the decals based on the data pointers...
dQsort(mDecalQueue.address(),
mDecalQueue.size(),
sizeof(DecalInstance*),
cmpDecalInstance);
mQueueDirty = false;
}
return false;
}
void DecalManager::renderObject(SceneState* state, SceneRenderImage*)
{
if (!smDecalsOn) return;
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
state->setupBaseProjection();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
renderDecal();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
void DecalManager::renderDecal()
{
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.1f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
DecalData* pLastData = NULL;
for (S32 x = 0; x < mDecalQueue.size(); x++)
{
if (mDecalQueue[x]->decalData != pLastData)
{
glBindTexture(GL_TEXTURE_2D, mDecalQueue[x]->decalData->textureHandle.getGLName());
pLastData = mDecalQueue[x]->decalData;
}
glColor4f(1, 1, 1, mDecalQueue[x]->fade);
glBegin(GL_TRIANGLE_FAN); {
glTexCoord2f(0, 0); glVertex3fv(mDecalQueue[x]->point[0]);
glTexCoord2f(0, 1); glVertex3fv(mDecalQueue[x]->point[1]);
glTexCoord2f(1, 1); glVertex3fv(mDecalQueue[x]->point[2]);
glTexCoord2f(1, 0); glVertex3fv(mDecalQueue[x]->point[3]);
} glEnd();
}
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glDisable(GL_ALPHA_TEST);
}

93
sim/decalManager.h Normal file
View file

@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _DECALMANAGER_H_
#define _DECALMANAGER_H_
#ifndef _SCENEOBJECT_H_
#include "Sim/sceneObject.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
class DecalData : public SimDataBlock
{
typedef SimDataBlock Parent;
//-------------------------------------- Console set variables
public:
F32 sizeX;
F32 sizeY;
StringTableEntry textureName;
//-------------------------------------- load set variables
public:
TextureHandle textureHandle;
public:
DecalData();
~DecalData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, char errorBuffer[256]);
DECLARE_CONOBJECT(DecalData);
static void initPersistFields();
};
struct DecalInstance
{
DecalData* decalData;
Point3F point[4];
U32 allocTime;
F32 fade;
DecalInstance* next;
};
class DecalManager : public SceneObject
{
typedef SceneObject Parent;
Vector<DecalInstance*> mDecalQueue;
bool mQueueDirty;
static U32 smMaxNumDecals;
static U32 smDecalTimeout;
static const U32 csmFreePoolBlockSize;
Vector<DecalInstance*> mFreePoolBlocks;
DecalInstance* mFreePool;
protected:
void renderObject(SceneState* state, SceneRenderImage*);
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
DecalInstance* allocateDecalInstance();
void freeDecalInstance(DecalInstance*);
public:
DecalManager();
~DecalManager();
static void consoleInit();
void addDecal(Point3F pos, Point3F rot, Point3F normal, DecalData*);
void addDecal(Point3F pos, Point3F normal, DecalData*);
void dataDeleted(DecalData *data);
void renderDecal();
DECLARE_CONOBJECT(DecalManager);
static bool smDecalsOn;
};
extern DecalManager* gDecalManager;
#endif // _H_DecalManager

80
sim/frameAllocator.cc Normal file
View file

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Sim/frameAllocator.h"
#include "console/console.h"
U8* FrameAllocator::smBuffer = NULL;
U32 FrameAllocator::smWaterMark = 0;
U32 FrameAllocator::smHighWaterMark = 0;
#ifdef DEBUG
S32 sgMaxFrameAllocation = 0;
ConsoleFunction(getMaxFrameAllocation, S32, 1,1, "getMaxFrameAllocation();")
{
argc, argv;
return sgMaxFrameAllocation;
}
#endif
void FrameAllocator::init(const U32 frameSize)
{
AssertFatal(smBuffer == NULL, "Error, already initialized");
smBuffer = new U8[frameSize];
smWaterMark = 0;
smHighWaterMark = frameSize;
}
void FrameAllocator::destroy()
{
AssertFatal(smBuffer != NULL, "Error, not initialized");
delete [] smBuffer;
smBuffer = NULL;
smWaterMark = 0;
smHighWaterMark = 0;
}
void* FrameAllocator::alloc(const U32 allocSize)
{
AssertFatal(smBuffer != NULL, "Error, no buffer!");
AssertFatal(smWaterMark + allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!");
U8* p = &smBuffer[smWaterMark];
smWaterMark += allocSize;
#ifdef DEBUG
if (smWaterMark > sgMaxFrameAllocation)
sgMaxFrameAllocation = smWaterMark;
#endif
return p;
}
void FrameAllocator::setWaterMark(const U32 waterMark)
{
AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark");
smWaterMark = waterMark;
}
U32 FrameAllocator::getWaterMark()
{
return smWaterMark;
}
U32 FrameAllocator::getHighWaterMark()
{
return smHighWaterMark;
}

33
sim/frameAllocator.h Normal file
View file

@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _FRAMEALLOCATOR_H_
#define _FRAMEALLOCATOR_H_
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
class FrameAllocator
{
static U8* smBuffer;
static U32 smHighWaterMark;
static U32 smWaterMark;
public:
static void init(const U32 frameSize);
static void destroy();
static void* alloc(const U32 allocSize);
static void setWaterMark(const U32);
static U32 getWaterMark();
static U32 getHighWaterMark();
};
#endif // _H_FRAMEALLOCATOR_

1164
sim/netConnection.cc Normal file

File diff suppressed because it is too large Load diff

526
sim/netConnection.h Normal file
View file

@ -0,0 +1,526 @@
//-----------------------------------------------------------------------------
// 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

270
sim/netDownload.cc Normal file
View file

@ -0,0 +1,270 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2002 GarageGames.Com
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dnet.h"
#include "console/simBase.h"
#include "sim/netConnection.h"
#include "core/bitStream.h"
#include "sim/netObject.h"
#include "core/resManager.h"
class DownloadMessageEvent : public NetEvent
{
U32 value;
S32 message;
public:
DownloadMessageEvent(S32 msg=0, U32 val=0)
{ message = msg; value = val; }
void pack(NetConnection *, BitStream *bstream)
{
bstream->write(value);
bstream->writeInt(message, 3);
}
void write(NetConnection *, BitStream *bstream)
{
bstream->write(value);
bstream->writeInt(message, 3);
}
void unpack(NetConnection *, BitStream *bstream)
{
bstream->read(&value);
message = bstream->readInt(3);
}
void process(NetConnection *ps)
{
ps->handleDownloadMessage(message, value);
}
DECLARE_CONOBJECT(DownloadMessageEvent);
};
IMPLEMENT_CO_NETEVENT_V1(DownloadMessageEvent);
class FileDownloadRequestEvent : public NetEvent
{
public:
enum {
MaxFileNames = 31,
};
U32 nameCount;
char mFileNames[MaxFileNames][256];
FileDownloadRequestEvent(Vector<char *> *nameList = NULL)
{
nameCount = 0;
if(nameList)
{
nameCount = nameList->size();
if(nameCount > MaxFileNames)
nameCount = MaxFileNames;
for(U32 i = 0; i < nameCount; i++)
{
dStrcpy(mFileNames[i], (*nameList)[i]);
Con::printf("Sending request for file %s", mFileNames[i]);
}
}
}
virtual void pack(NetConnection *, BitStream *bstream)
{
bstream->writeRangedU32(nameCount, 0, MaxFileNames);
for(U32 i = 0; i < nameCount; i++)
bstream->writeString(mFileNames[i]);
}
virtual void write(NetConnection *, BitStream *bstream)
{
bstream->writeRangedU32(nameCount, 0, MaxFileNames);
for(U32 i = 0; i < nameCount; i++)
bstream->writeString(mFileNames[i]);
}
virtual void unpack(NetConnection *, BitStream *bstream)
{
nameCount = bstream->readRangedU32(0, MaxFileNames);
for(U32 i = 0; i < nameCount; i++)
bstream->readString(mFileNames[i]);
}
virtual void process(NetConnection *connection)
{
U32 i;
for(i = 0; i < nameCount; i++)
if(connection->startSendingFile(mFileNames[i]))
break;
if(i == nameCount)
connection->startSendingFile(NULL); // none of the files were sent
}
DECLARE_CONOBJECT(FileDownloadRequestEvent);
};
IMPLEMENT_CO_NETEVENT_V1(FileDownloadRequestEvent);
class FileChunkEvent : public NetEvent
{
public:
enum
{
ChunkSize = 63,
};
U8 chunkData[ChunkSize];
U32 chunkLen;
FileChunkEvent(U8 *data = NULL, U32 len = 0)
{
if(data)
dMemcpy(chunkData, data, len);
chunkLen = len;
}
virtual void pack(NetConnection *, BitStream *bstream)
{
bstream->writeRangedU32(chunkLen, 0, ChunkSize);
bstream->write(chunkLen, chunkData);
}
virtual void write(NetConnection *, BitStream *bstream)
{
bstream->writeRangedU32(chunkLen, 0, ChunkSize);
bstream->write(chunkLen, chunkData);
}
virtual void unpack(NetConnection *, BitStream *bstream)
{
chunkLen = bstream->readRangedU32(0, ChunkSize);
bstream->read(chunkLen, chunkData);
}
virtual void process(NetConnection *connection)
{
connection->chunkReceived(chunkData, chunkLen);
}
virtual void notifyDelivered(NetConnection *nc, bool madeIt)
{
if(!nc->isRemoved())
nc->sendFileChunk();
}
DECLARE_CONOBJECT(FileChunkEvent);
};
IMPLEMENT_CO_NETEVENT_V1(FileChunkEvent);
void NetConnection::sendFileChunk()
{
U8 buffer[FileChunkEvent::ChunkSize];
U32 len = FileChunkEvent::ChunkSize;
if(len + mCurrentFileBufferOffset > mCurrentFileBufferSize)
len = mCurrentFileBufferSize - mCurrentFileBufferOffset;
if(!len)
{
ResourceManager->closeStream(mCurrentDownloadingFile);
mCurrentDownloadingFile = NULL;
return;
}
mCurrentFileBufferOffset += len;
mCurrentDownloadingFile->read(len, buffer);
postNetEvent(new FileChunkEvent(buffer, len));
}
bool NetConnection::startSendingFile(const char *fileName)
{
if(!fileName)
{
postNetEvent(new DownloadMessageEvent(SendNextRequest));
return false;
}
mCurrentDownloadingFile = ResourceManager->openStream(fileName);
if(!mCurrentDownloadingFile)
{
// the server didn't have the file, so send a 0 byte chunk:
Con::printf("No such file %s.", fileName);
postNetEvent(new FileChunkEvent(NULL, 0));
return false;
}
Con::printf("Sending file %s.", fileName);
mCurrentFileBufferSize = mCurrentDownloadingFile->getStreamSize();
mCurrentFileBufferOffset = 0;
// always have 32 file chunks (64 bytes each) in transit
postNetEvent(new DownloadMessageEvent(FileSizeMessage, mCurrentFileBufferSize));
for(U32 i = 0; i < 32; i++)
sendFileChunk();
return true;
}
void NetConnection::sendNextFileDownloadRequest()
{
// see if we've already downloaded this file...
while(mMissingFileList.size() && ResourceManager->find(mMissingFileList[0]))
{
dFree(mMissingFileList[0]);
mMissingFileList.pop_front();
}
if(mMissingFileList.size())
{
postNetEvent(new FileDownloadRequestEvent(&mMissingFileList));
}
else
{
fileDownloadSegmentComplete();
}
}
void NetConnection::chunkReceived(U8 *chunkData, U32 chunkLen)
{
if(chunkLen == 0)
{
// the server didn't have the file... apparently it's one we don't need...
dFree(mCurrentFileBuffer);
mCurrentFileBuffer = NULL;
dFree(mMissingFileList[0]);
mMissingFileList.pop_front();
return;
}
if(chunkLen + mCurrentFileBufferOffset > mCurrentFileBufferSize)
{
setLastError("Invalid file chunk from server.");
return;
}
dMemcpy(((U8 *) mCurrentFileBuffer) + mCurrentFileBufferOffset, chunkData, chunkLen);
mCurrentFileBufferOffset += chunkLen;
if(mCurrentFileBufferOffset == mCurrentFileBufferSize)
{
// this file's done...
// save it to disk:
FileStream stream;
Con::printf("Saving file %s.", mMissingFileList[0]);
if(!ResourceManager->openFileForWrite(stream, mMissingFileList[0]))
{
setLastError("Couldn't open file downloaded by server.");
return;
}
dFree(mMissingFileList[0]);
mMissingFileList.pop_front();
stream.write(mCurrentFileBufferSize, mCurrentFileBuffer);
stream.close();
dFree(mCurrentFileBuffer);
mCurrentFileBuffer = NULL;
sendNextFileDownloadRequest();
}
else
{
Con::executef(4, "onFileChunkReceived", mMissingFileList[0], Con::getIntArg(mCurrentFileBufferOffset), Con::getIntArg(mCurrentFileBufferSize));
}
}
void NetConnection::handleDownloadMessage(S32 message, S32 value)
{
if((message == SendNextRequest || message == FileSizeMessage ) && !mGhostTo)
{
setLastError("Invalid packet.");
return;
}
switch(message)
{
case SendNextRequest:
sendNextFileDownloadRequest();
break;
case FileSizeMessage:
mCurrentFileBufferSize = value;
mCurrentFileBuffer = dRealloc(mCurrentFileBuffer, mCurrentFileBufferSize);
mCurrentFileBufferOffset = 0;
break;
}
}

355
sim/netEvent.cc Normal file
View file

@ -0,0 +1,355 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dnet.h"
#include "console/simBase.h"
#include "sim/netConnection.h"
#include "core/bitStream.h"
#define DebugChecksum 0xF00DBAAD
void NetEvent::notifyDelivered(NetConnection *, bool)
{
}
void NetEvent::notifySent(NetConnection *)
{
}
#ifdef DEBUG_NET
const char *NetEvent::getDebugName()
{
return getClassName();
}
#endif
void NetConnection::eventPacketDropped(PacketNotify *notify)
{
NetEvent *walk = notify->eventList;
NetEvent **insertList = &mSendEventQueueHead;
NetEvent *temp;
while(walk)
{
switch(walk->mGuaranteeType)
{
case NetEvent::GuaranteedOrdered:
//Con::printf("EVT %d: DROP - %d", getId(), walk->mSeqCount);
while(*insertList && (*insertList)->mSeqCount < walk->mSeqCount)
insertList = &((*insertList)->mNextEvent);
temp = walk->mNextEvent;
walk->mNextEvent = *insertList;
if(!walk->mNextEvent)
mSendEventQueueTail = walk;
*insertList = walk;
insertList = &(walk->mNextEvent);
walk = temp;
break;
case NetEvent::Guaranteed:
temp = walk->mNextEvent;
walk->mNextEvent = mUnorderedSendEventQueueHead;
mUnorderedSendEventQueueHead = walk;
if(!walk->mNextEvent)
mUnorderedSendEventQueueTail = walk;
walk = temp;
break;
case NetEvent::Unguaranteed:
walk->notifyDelivered(this, false);
temp = walk->mNextEvent;
delete walk;
walk = temp;
}
}
}
void NetConnection::eventPacketReceived(PacketNotify *notify)
{
NetEvent *walk = notify->eventList;
NetEvent **noteList = &mNotifyEventList;
while(walk)
{
NetEvent *next = walk->mNextEvent;
if(walk->mGuaranteeType != NetEvent::GuaranteedOrdered)
{
walk->notifyDelivered(this, true);
delete walk;
walk = next;
}
else
{
while(*noteList && (*noteList)->mSeqCount < walk->mSeqCount)
noteList = &((*noteList)->mNextEvent);
walk->mNextEvent = *noteList;
*noteList = walk;
noteList = &walk->mNextEvent;
walk = next;
}
}
while(mNotifyEventList && mNotifyEventList->mSeqCount == mLastAckedEventSeq + 1)
{
mLastAckedEventSeq++;
NetEvent *next = mNotifyEventList->mNextEvent;
//Con::printf("EVT %d: ACK - %d", getId(), mNotifyEventList->mSeqCount);
mNotifyEventList->notifyDelivered(this, true);
delete mNotifyEventList;
mNotifyEventList = next;
}
}
void NetConnection::eventWritePacket(BitStream *bstream, PacketNotify *notify)
{
#ifdef DEBUG_NET
bstream->writeInt(DebugChecksum, 32);
#endif
NetEvent *packQueueHead = NULL, *packQueueTail = NULL;
while(mUnorderedSendEventQueueHead)
{
if(bstream->isFull())
break;
// dequeue the first event
NetEvent *ev = mUnorderedSendEventQueueHead;
mUnorderedSendEventQueueHead = ev->mNextEvent;
U32 start = bstream->getCurPos();
bstream->writeFlag(true);
S32 classId = ev->getClassId();
AssertFatal(classId >= NetEventClassFirst && classId <= NetEventClassLast,
"Out of range event class id... check simBase.h");
bstream->writeInt(classId - NetEventClassFirst, NetEventClassBitSize);
ev->pack(this, bstream);
DEBUG_LOG(("PKLOG %d EVENT %d: %s", getId(), bstream->getCurPos() - start, ev->getDebugName()) );
#ifdef DEBUG_NET
bstream->writeInt(classId, 10);
bstream->writeInt(classId ^ DebugChecksum, 32);
#endif
// add this event onto the packet queue
ev->mNextEvent = NULL;
if(!packQueueHead)
packQueueHead = ev;
else
packQueueTail->mNextEvent = ev;
packQueueTail = ev;
}
bstream->writeFlag(false);
S32 prevSeq = -2;
while(mSendEventQueueHead)
{
if(bstream->isFull())
break;
// if the event window is full, stop processing
if(mSendEventQueueHead->mSeqCount > mLastAckedEventSeq + 126)
break;
// dequeue the first event
NetEvent *ev = mSendEventQueueHead;
mSendEventQueueHead = ev->mNextEvent;
//Con::printf("EVT %d: SEND - %d", getId(), ev->mSeqCount);
bstream->writeFlag(true);
ev->mNextEvent = NULL;
if(!packQueueHead)
packQueueHead = ev;
else
packQueueTail->mNextEvent = ev;
packQueueTail = ev;
if(!bstream->writeFlag(ev->mSeqCount == prevSeq + 1))
bstream->writeInt(ev->mSeqCount, 7);
prevSeq = ev->mSeqCount;
U32 start = bstream->getCurPos();
S32 classId = ev->getClassId();
AssertFatal(classId >= NetEventClassFirst && classId <= NetEventClassLast,
"Out of range event class id... check simBase.h");
bstream->writeInt(classId - NetEventClassFirst, NetEventClassBitSize);
ev->pack(this, bstream);
DEBUG_LOG(("PKLOG %d EVENT %d: %s", getId(), bstream->getCurPos() - start, ev->getDebugName()) );
#ifdef DEBUG_NET
bstream->writeInt(classId, 10);
bstream->writeInt(classId ^ DebugChecksum, 32);
#endif
}
for(NetEvent *ev = packQueueHead; ev; ev = ev->mNextEvent)
ev->notifySent(this);
notify->eventList = packQueueHead;
bstream->writeFlag(0);
}
void NetConnection::eventReadPacket(BitStream *bstream)
{
#ifdef DEBUG_NET
U32 sum = bstream->readInt(32);
AssertISV(sum == DebugChecksum, "Invalid checksum.");
#endif
S32 prevSeq = -2;
NetEvent **waitInsert = &mWaitSeqEvents;
bool unguaranteedPhase = true;
while(true)
{
bool bit = bstream->readFlag();
if(unguaranteedPhase && !bit)
{
unguaranteedPhase = false;
bit = bstream->readFlag();
}
if(!unguaranteedPhase && !bit)
break;
S32 seq = -1;
if(!unguaranteedPhase) // get the sequence
{
if(bstream->readFlag())
seq = (prevSeq + 1) & 0x7f;
else
seq = bstream->readInt(7);
prevSeq = seq;
}
S32 classTag = bstream->readInt(NetEventClassBitSize) + NetEventClassFirst;
NetEvent *evt = (NetEvent *) ConsoleObject::create(classTag);
if(!evt)
{
setLastError("Invalid packet.");
return;
}
AbstractClassRep *rep = evt->getClassRep();
if((rep->mClassNetClass == NetEventClassClient && !isServerConnection())
|| (rep->mClassNetClass == NetEventClassServer && isServerConnection()) )
{
setLastError("Invalid Packet.");
return;
}
evt->mSourceId = getId();
evt->unpack(this, bstream);
if(mErrorBuffer[0])
return;
#ifdef DEBUG_NET
U32 classId = bstream->readInt(10);
U32 checksum = bstream->readInt(32);
AssertISV( (checksum ^ DebugChecksum) == (U32)classTag,
avar("unpack did not match pack for event of class %s.",
evt->getClassName()) );
#endif
if(unguaranteedPhase)
{
evt->process(this);
delete evt;
if(mErrorBuffer[0])
return;
continue;
}
seq |= (mNextRecvEventSeq & ~0x7F);
if(seq < mNextRecvEventSeq)
seq += 128;
evt->mSeqCount = seq;
//Con::printf("EVT %d: RECV - %d", getId(), evt->mSeqCount);
while(*waitInsert && (*waitInsert)->mSeqCount < seq)
waitInsert = &((*waitInsert)->mNextEvent);
evt->mNextEvent = *waitInsert;
*waitInsert = evt;
waitInsert = &(evt->mNextEvent);
}
while(mWaitSeqEvents && mWaitSeqEvents->mSeqCount == mNextRecvEventSeq)
{
mNextRecvEventSeq++;
NetEvent *temp = mWaitSeqEvents;
mWaitSeqEvents = temp->mNextEvent;
//Con::printf("EVT %d: PROCESS - %d", getId(), temp->mSeqCount);
temp->process(this);
delete temp;
if(mErrorBuffer[0])
return;
}
}
bool NetConnection::postNetEvent(NetEvent *event)
{
AssertFatal(event->mIsPosted == false, "Event cannot be posted to more than one connection.");
event->mIsPosted = true;
if(!mSendingEvents)
{
delete event;
return false;
}
event->mNextEvent = NULL;
if(event->mGuaranteeType == NetEvent::GuaranteedOrdered)
{
event->mSeqCount = mNextSendEventSeq++;
if(!mSendEventQueueHead)
mSendEventQueueHead = event;
else
mSendEventQueueTail->mNextEvent = event;
mSendEventQueueTail = event;
}
else
{
event->mSeqCount = InvalidSendEventSeq;
if(!mUnorderedSendEventQueueHead)
mUnorderedSendEventQueueHead = event;
else
mUnorderedSendEventQueueTail->mNextEvent = event;
mUnorderedSendEventQueueTail = event;
}
return true;
}
void NetConnection::eventWriteStartBlock(ResizeBitStream *stream)
{
stream->write(mNextRecvEventSeq);
for(NetEvent *walk = mWaitSeqEvents; walk; walk = walk->mNextEvent)
{
stream->writeFlag(true);
S32 classId = walk->getClassId();
stream->writeInt(classId - NetEventClassFirst, NetEventClassBitSize);
walk->write(this, stream);
stream->validate();
}
stream->writeFlag(false);
}
void NetConnection::eventReadStartBlock(BitStream *stream)
{
stream->read(&mNextRecvEventSeq);
NetEvent *lastEvent = NULL;
while(stream->readFlag())
{
S32 classTag = stream->readInt(NetEventClassBitSize) + NetEventClassFirst;
NetEvent *evt = (NetEvent *) ConsoleObject::create(classTag);
evt->unpack(this, stream);
evt->mNextEvent = NULL;
if(!lastEvent)
mWaitSeqEvents = evt;
else
lastEvent->mNextEvent = evt;
lastEvent = evt;
}
}

908
sim/netGhost.cc Normal file
View file

@ -0,0 +1,908 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dnet.h"
#include "console/simBase.h"
#include "sim/netConnection.h"
#include "core/bitStream.h"
#include "sim/netObject.h"
#define DebugChecksum 0xF00D
class GhostingMessageEvent : public NetEvent
{
U32 sequence;
S32 message;
U32 ghostCount;
public:
GhostingMessageEvent(S32 msg=0, U32 seq=0, U32 gc=0)
{ message = msg; sequence = seq; ghostCount = gc;}
void pack(NetConnection *, BitStream *bstream)
{
bstream->write(sequence);
bstream->writeInt(message, 3);
bstream->writeInt(ghostCount, 11);
}
void write(NetConnection *, BitStream *bstream)
{
bstream->write(sequence);
bstream->writeInt(message, 3);
bstream->writeInt(ghostCount, 11);
}
void unpack(NetConnection *, BitStream *bstream)
{
bstream->read(&sequence);
message = bstream->readInt(3);
ghostCount = bstream->readInt(11);
}
void process(NetConnection *ps)
{
ps->handleGhostMessage(message, sequence, ghostCount);
}
DECLARE_CONOBJECT(GhostingMessageEvent);
};
IMPLEMENT_CO_NETEVENT_V1(GhostingMessageEvent);
class GhostAlwaysObjectEvent : public NetEvent
{
SimObjectId objectId;
U32 ghostIndex;
NetObject *object;
bool validObject;
public:
GhostAlwaysObjectEvent(NetObject *obj = NULL, U32 index = 0)
{
if(obj)
{
objectId = obj->getId();
ghostIndex = index;
}
object = NULL;
}
~GhostAlwaysObjectEvent()
{ delete object; }
void pack(NetConnection *ps, BitStream *bstream)
{
bstream->writeInt(ghostIndex, 10);
NetObject *obj = (NetObject *) Sim::findObject(objectId);
if(bstream->writeFlag(obj != NULL))
{
S32 classId = obj->getClassId();
AssertFatal(classId >= NetObjectClassFirst && classId <= NetObjectClassLast,
"Out of range event class id... check simBase.h");
bstream->writeInt(classId - NetObjectClassFirst, NetObjectClassBitSize);
obj->packUpdate(ps, 0xFFFFFFFF, bstream);
}
}
void write(NetConnection *ps, BitStream *bstream)
{
bstream->writeInt(ghostIndex, 10);
if(bstream->writeFlag(validObject))
{
S32 classId = object->getClassId();
bstream->writeInt(classId - NetObjectClassFirst, NetObjectClassBitSize);
object->packUpdate(ps, 0xFFFFFFFF, bstream);
}
}
void unpack(NetConnection *ps, BitStream *bstream)
{
ghostIndex = bstream->readInt(10);
if(bstream->readFlag())
{
S32 classId = bstream->readInt(NetObjectClassBitSize) + NetObjectClassFirst;
object = (NetObject *) ConsoleObject::create(classId);
if(!object)
{
ps->setLastError("Invalid packet.");
return;
}
object->mNetFlags = NetObject::IsGhost;
object->unpackUpdate(ps, bstream);
validObject = true;
}
else
{
object = new NetObject;
validObject = false;
}
}
void process(NetConnection *ps)
{
Con::executef(1, "ghostAlwaysObjectReceived");
ps->setGhostAlwaysObject(object, ghostIndex);
object = NULL;
}
DECLARE_CONOBJECT(GhostAlwaysObjectEvent);
};
IMPLEMENT_CO_NETEVENT_V1(GhostAlwaysObjectEvent);
void NetConnection::ghostPacketDropped(PacketNotify *notify)
{
GhostRef *packRef = notify->ghostList;
// loop through all the packRefs in the packet
while(packRef)
{
GhostRef *temp = packRef->nextRef;
U32 orFlags = 0;
AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");
// clear out the ref for this object, plus or together all
// flags from updates after this
GhostRef **walk = &(packRef->ghost->updateChain);
while(*walk != packRef)
{
orFlags |= (*walk)->mask;
walk = &((*walk)->nextUpdateChain);
}
*walk = 0;
// for any flags we haven't updated since this (dropped) packet
// or them into the mask so they'll get updated soon
orFlags = packRef->mask & ~orFlags;
if(orFlags)
{
if(!packRef->ghost->updateMask)
{
packRef->ghost->updateMask = orFlags;
ghostPushNonZero(packRef->ghost);
}
else
packRef->ghost->updateMask |= orFlags;
}
// if this packet was ghosting an object, set it
// to re ghost at it's earliest convenience
if(packRef->ghostInfoFlags & GhostInfo::Ghosting)
{
packRef->ghost->flags |= GhostInfo::NotYetGhosted;
packRef->ghost->flags &= ~GhostInfo::Ghosting;
}
// otherwise, if it was being deleted,
// set it to re-delete
else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)
{
packRef->ghost->flags |= GhostInfo::KillGhost;
packRef->ghost->flags &= ~GhostInfo::KillingGhost;
}
delete packRef;
packRef = temp;
}
}
void NetConnection::ghostPacketReceived(PacketNotify *notify)
{
GhostRef *packRef = notify->ghostList;
// loop through all the notifies in this packet
while(packRef)
{
GhostRef *temp = packRef->nextRef;
AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");
// clear this notify from the end of the object's notify
// chain
GhostRef **walk = &(packRef->ghost->updateChain);
while(*walk != packRef)
walk = &((*walk)->nextUpdateChain);
*walk = 0;
// if this object was ghosting , it is now ghosted
if(packRef->ghostInfoFlags & GhostInfo::Ghosting)
packRef->ghost->flags &= ~GhostInfo::Ghosting;
// otherwise, if it was dieing, free the ghost
else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)
freeGhostInfo(packRef->ghost);
delete packRef;
packRef = temp;
}
}
struct UpdateQueueEntry
{
F32 priority;
GhostInfo *obj;
UpdateQueueEntry(F32 in_priority, GhostInfo *in_obj)
{ priority = in_priority; obj = in_obj; }
};
static S32 QSORT_CALLBACK UQECompare(const void *a,const void *b)
{
GhostInfo *ga = *((GhostInfo **) a);
GhostInfo *gb = *((GhostInfo **) b);
F32 ret = ga->priority - gb->priority;
return (ret < 0) ? -1 : ((ret > 0) ? 1 : 0);
}
void NetConnection::ghostWritePacket(BitStream *bstream, PacketNotify *notify)
{
#ifdef DEBUG_NET
bstream->writeInt(DebugChecksum, 32);
#endif
notify->ghostList = NULL;
if(!mGhostFrom)
return;
if(!bstream->writeFlag(mGhosting))
return;
// fill a packet (or two) with ghosting data
// first step is to check all our polled ghosts:
// 1. Scope query - find if any new objects have come into
// scope and if any have gone out.
// 2. call scoped objects' priority functions if the flag set is nonzero
// A removed ghost is assumed to have a high priority
// 3. call updates based on sorted priority until the packet is
// full. set flags to zero for all updated objects
CameraScopeQuery camInfo;
camInfo.camera = NULL;
camInfo.pos.set(0,0,0);
camInfo.orientation.set(0,1,0);
camInfo.visibleDistance = 1;
camInfo.fov = (F32)(3.1415f / 4.0f);
camInfo.sinFov = 0.7071f;
camInfo.cosFov = 0.7071f;
GhostInfo *walk;
// only need to worry about the ghosts that have update masks set...
S32 maxIndex = 0;
S32 i;
for(i = 0; i < mGhostZeroUpdateIndex; i++)
{
// increment the updateSkip for everyone... it's all good
walk = mGhostArray[i];
walk->updateSkipCount++;
if(!(walk->flags & (GhostInfo::ScopeAlways | GhostInfo::ScopeLocalAlways)))
walk->flags &= ~GhostInfo::InScope;
}
if(mScopeObject)
mScopeObject->onCameraScopeQuery(this, &camInfo);
for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
{
if(!(mGhostArray[i]->flags & GhostInfo::InScope))
detachObject(mGhostArray[i]);
}
for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
{
walk = mGhostArray[i];
if(walk->index > maxIndex)
maxIndex = walk->index;
// clear out any kill objects that haven't been ghosted yet
if((walk->flags & GhostInfo::KillGhost) && (walk->flags & GhostInfo::NotYetGhosted))
{
freeGhostInfo(walk);
continue;
}
// don't do any ghost processing on objects that are being killed
// or in the process of ghosting
else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)))
{
if(walk->flags & GhostInfo::KillGhost)
walk->priority = 10000;
else
walk->priority = walk->obj->getUpdatePriority(&camInfo, walk->updateMask, walk->updateSkipCount);
}
else
walk->priority = 0;
}
GhostRef *updateList = NULL;
dQsort(mGhostArray, mGhostZeroUpdateIndex, sizeof(GhostInfo *), UQECompare);
// reset the array indices...
for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
mGhostArray[i]->arrayIndex = i;
S32 sendSize = 1;
while(maxIndex >>= 1)
sendSize++;
if(sendSize < 3)
sendSize = 3;
bstream->writeInt(sendSize - 3, 3); // 0-7 3 bit number
U32 count = 0;
//
for(i = mGhostZeroUpdateIndex - 1; i >= 0 && !bstream->isFull(); i--)
{
GhostInfo *walk = mGhostArray[i];
if(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting))
continue;
//S32 startPos = bstream->getCurPos();
bstream->writeFlag(true);
bstream->writeInt(walk->index, sendSize);
U32 updateMask = walk->updateMask;
GhostRef *upd = new GhostRef;
upd->nextRef = updateList;
updateList = upd;
upd->nextUpdateChain = walk->updateChain;
walk->updateChain = upd;
upd->ghost = walk;
upd->ghostInfoFlags = 0;
if(walk->flags & GhostInfo::KillGhost)
{
walk->flags &= ~GhostInfo::KillGhost;
walk->flags |= GhostInfo::KillingGhost;
walk->updateMask = 0;
upd->mask = updateMask;
ghostPushToZero(walk);
upd->ghostInfoFlags = GhostInfo::KillingGhost;
bstream->writeFlag(true); // killing ghost
}
else
{
bstream->writeFlag(false);
U32 startPos = bstream->getCurPos();
if(walk->flags & GhostInfo::NotYetGhosted)
{
S32 classId = walk->obj->getClassId();
AssertFatal(classId >= NetObjectClassFirst && classId <= NetObjectClassLast, "Bad ghost tag.");
walk->flags &= ~GhostInfo::NotYetGhosted;
walk->flags |= GhostInfo::Ghosting;
upd->ghostInfoFlags = GhostInfo::Ghosting;
bstream->writeInt(classId - NetObjectClassFirst, NetObjectClassBitSize);
}
#ifdef DEBUG_NET
bstream->writeInt(walk->obj->getClassId(), 10);
bstream->writeInt(walk->obj->getClassId() ^ DebugChecksum, 16);
#endif
// update the object
U32 retMask = walk->obj->packUpdate(this, updateMask, bstream);
DEBUG_LOG(("PKLOG %d GHOST %d: %s", getId(), bstream->getCurPos() - 16 - startPos, walk->obj->getClassName()));
AssertFatal((retMask & (~updateMask)) == 0, "Cannot set new bits in packUpdate return");
walk->updateMask = retMask;
if(!retMask)
ghostPushToZero(walk);
upd->mask = updateMask & ~retMask;
//PacketStream::getStats()->addBits(PacketStats::Send, bstream->getCurPos() - startPos, walk->obj->getPersistTag());
#ifdef DEBUG_NET
bstream->writeInt(walk->index ^ DebugChecksum, 16);
#endif
}
walk->updateSkipCount = 0;
count++;
}
//Con::printf("Ghosts updated: %d (%d remain)", count, mGhostZeroUpdateIndex);
// no more objects...
bstream->writeFlag(false);
notify->ghostList = updateList;
}
void NetConnection::ghostReadPacket(BitStream *bstream)
{
#ifdef DEBUG_NET
U32 sum = bstream->readInt(32);
AssertISV(sum == DebugChecksum, "Invalid checksum.");
#endif
if(!mGhostTo)
return;
if(!bstream->readFlag())
return;
S32 idSize;
idSize = bstream->readInt( 3);
idSize += 3;
// while there's an object waiting...
NetConnection *remoteConnection = NULL;
if(mConnectionObjectId)
remoteConnection = (NetConnection *) Sim::findObject(mConnectionObjectId);
while(bstream->readFlag())
{
U32 index;
//S32 startPos = bstream->getCurPos();
index = (U32) bstream->readInt(idSize);
if(bstream->readFlag()) // is this ghost being deleted?
{
AssertFatal(mLocalGhosts[index] != NULL, "Error, NULL ghost encountered.");
mLocalGhosts[index]->deleteObject();
mLocalGhosts[index] = NULL;
}
else
{
if(!mLocalGhosts[index]) // it's a new ghost... cool
{
U32 tag;
tag = (U32) bstream->readInt(NetObjectClassBitSize) + NetObjectClassFirst;
NetObject *obj = (NetObject *) ConsoleObject::create(tag);
if(!obj)
{
setLastError("Invalid packet.");
return;
}
obj->mNetFlags = NetObject::IsGhost;
// object gets initial update before adding to the manager
obj->mNetIndex = index;
mLocalGhosts[index] = obj;
#ifdef DEBUG_NET
U32 classId = bstream->readInt(10);
U32 checksum = bstream->readInt(16);
AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");
AssertISV( (checksum ^ DebugChecksum) == mLocalGhosts[index]->getClassId(),
avar("class id mismatch for dest class %s.",
mLocalGhosts[index]->getClassName()) );
#endif
mLocalGhosts[index]->unpackUpdate(this, bstream);
if(!obj->registerObject())
{
if(!mErrorBuffer[0])
setLastError("Invalid packet.");
return;
}
if(remoteConnection)
obj->mServerObject = remoteConnection->resolveGhostParent(index);
addObject(obj);
}
else
{
#ifdef DEBUG_NET
U32 classId = bstream->readInt(10);
U32 checksum = bstream->readInt(16);
AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");
AssertISV( (checksum ^ DebugChecksum) == mLocalGhosts[index]->getClassId(),
avar("class id mismatch for dest class %s.",
mLocalGhosts[index]->getClassName()) );
#endif
mLocalGhosts[index]->unpackUpdate(this, bstream);
}
//PacketStream::getStats()->addBits(PacketStats::Receive, bstream->getCurPos() - startPos, ghostRefs[index].localGhost->getPersistTag());
#ifdef DEBUG_NET
U32 checksum = bstream->readInt(16);
AssertISV( (checksum ^ DebugChecksum) == index,
avar("unpackUpdate did not match packUpdate for object of class %s.",
mLocalGhosts[index]->getClassName()) );
#endif
if(mErrorBuffer[0])
return;
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void NetConnection::setScopeObject(NetObject *obj)
{
if(((NetObject *) mScopeObject) == obj)
return;
mScopeObject = obj;
}
void NetConnection::detachObject(GhostInfo *info)
{
// mark it for ghost killin'
info->flags |= GhostInfo::KillGhost;
// if the mask is in the zero range, we've got to move it up...
if(!info->updateMask)
{
info->updateMask = 0xFFFFFFFF;
ghostPushNonZero(info);
}
if(info->obj)
{
if(info->prevObjectRef)
info->prevObjectRef->nextObjectRef = info->nextObjectRef;
else
info->obj->mFirstObjectRef = info->nextObjectRef;
if(info->nextObjectRef)
info->nextObjectRef->prevObjectRef = info->prevObjectRef;
// remove it from the lookup table
U32 id = info->obj->getId();
for(GhostInfo **walk = &mGhostLookupTable[id & (GhostLookupTableSize - 1)]; *walk; walk = &((*walk)->nextLookupInfo))
{
GhostInfo *temp = *walk;
if(temp == info)
{
*walk = temp->nextLookupInfo;
break;
}
}
info->prevObjectRef = info->nextObjectRef = NULL;
info->obj = NULL;
}
}
void NetConnection::freeGhostInfo(GhostInfo *ghost)
{
AssertFatal(ghost->arrayIndex < mGhostFreeIndex, "Ghost already freed.");
if(ghost->arrayIndex < mGhostZeroUpdateIndex)
{
AssertFatal(ghost->updateMask != 0, "Invalid ghost mask.");
ghost->updateMask = 0;
ghostPushToZero(ghost);
}
ghostPushZeroToFree(ghost);
AssertFatal(ghost->updateChain == NULL, "Ack!");
}
//-----------------------------------------------------------------------------
void NetConnection::objectLocalScopeAlways(NetObject *obj)
{
objectInScope(obj);
for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)
{
if(walk->obj != obj)
continue;
walk->flags |= GhostInfo::ScopeLocalAlways;
return;
}
}
void NetConnection::objectLocalClearAlways(NetObject *obj)
{
for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)
{
if(walk->obj != obj)
continue;
walk->flags &= ~GhostInfo::ScopeLocalAlways;
return;
}
}
bool NetConnection::validateGhostArray()
{
AssertFatal(mGhostZeroUpdateIndex >= 0 && mGhostZeroUpdateIndex <= mGhostFreeIndex, "Invalid update index range.");
AssertFatal(mGhostFreeIndex <= MaxGhostCount, "Invalid free index range.");
U32 i;
for(i = 0; i < mGhostZeroUpdateIndex; i ++)
{
AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
AssertFatal(mGhostArray[i]->updateMask != 0, "Invalid ghost mask.");
}
for(; i < mGhostFreeIndex; i ++)
{
AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
AssertFatal(mGhostArray[i]->updateMask == 0, "Invalid ghost mask.");
}
for(; i < MaxGhostCount; i++)
{
AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
}
return true;
}
void NetConnection::objectInScope(NetObject *obj)
{
if(!mScoping)
return;
if (obj->isScopeLocal() && !mConnectionObjectId)
return;
S32 index = obj->getId() & (GhostLookupTableSize - 1);
// check if it's already in scope
// the object may have been cleared out without the lookupTable being cleared
// so validate that the object pointers are the same.
for(GhostInfo *walk = mGhostLookupTable[index ]; walk; walk = walk->nextLookupInfo)
{
if(walk->obj != obj)
continue;
walk->flags |= GhostInfo::InScope;
return;
}
if (mGhostFreeIndex == MaxGhostCount)
return;
GhostInfo *giptr = mGhostArray[mGhostFreeIndex];
ghostPushFreeToZero(giptr);
giptr->updateMask = 0xFFFFFFFF;
ghostPushNonZero(giptr);
giptr->flags = GhostInfo::NotYetGhosted | GhostInfo::InScope;
if(obj->mNetFlags.test(NetObject::ScopeAlways))
giptr->flags |= GhostInfo::ScopeAlways;
giptr->obj = obj;
giptr->updateChain = NULL;
giptr->updateSkipCount = 0;
giptr->connection = this;
giptr->nextObjectRef = obj->mFirstObjectRef;
if(obj->mFirstObjectRef)
obj->mFirstObjectRef->prevObjectRef = giptr;
giptr->prevObjectRef = NULL;
obj->mFirstObjectRef = giptr;
giptr->nextLookupInfo = mGhostLookupTable[index];
mGhostLookupTable[index] = giptr;
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
}
//-----------------------------------------------------------------------------
void NetConnection::handleGhostMessage(S32 message, U32 sequence, U32 ghostCount)
{
if((message == GhostAlwaysStarting || message == GhostAlwaysDone || message == EndGhosting) && !mGhostTo)
{
setLastError("Invalid packet.");
return;
}
S32 i;
switch(message)
{
case GhostAlwaysDone:
mGhostingSequence = sequence;
postNetEvent(new GhostingMessageEvent(ReadyForNormalGhosts, sequence));
break;
case ReadyForNormalGhosts:
if(sequence != mGhostingSequence)
return;
mGhosting = true;
for(i = 0; i < mGhostFreeIndex; i++)
{
if(mGhostArray[i]->flags & GhostInfo::ScopedEvent)
mGhostArray[i]->flags &= ~(GhostInfo::Ghosting | GhostInfo::ScopedEvent);
}
break;
case EndGhosting:
// just delete all the local ghosts
for(i = 0; i < MaxGhostCount; i++)
{
if(mLocalGhosts[i])
{
mLocalGhosts[i]->deleteObject();
mLocalGhosts[i] = NULL;
}
}
break;
case GhostAlwaysStarting:
Con::executef(2, "ghostAlwaysStarted", Con::getIntArg(ghostCount));
break;
}
}
void NetConnection::activateGhosting()
{
if(!mGhostFrom)
return;
mGhostingSequence++;
// iterate through the ghost always objects and InScope them...
// also post em all to the other side.
SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet();
SimSet::iterator i;
AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Error: ghosts in the ghost list before activate.");
U32 sz = ghostAlwaysSet->size();
S32 j;
for(j = 0; j < sz; j++)
{
U32 idx = MaxGhostCount - sz + j;
mGhostArray[j] = mGhostRefs + idx;
mGhostArray[j]->arrayIndex = j;
}
for(j = sz; j < MaxGhostCount; j++)
{
U32 idx = j - sz;
mGhostArray[j] = mGhostRefs + idx;
mGhostArray[j]->arrayIndex = j;
}
mScoping = true; // so that objectInScope will work
for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++)
{
AssertFatal(dynamic_cast<NetObject *>(*i) != NULL, avar("Non NetObject in GhostAlwaysSet: %s", (*i)->getClassName()));
NetObject *obj = (NetObject *)(*i);
if(obj->mNetFlags.test(NetObject::Ghostable))
objectInScope(obj);
}
postNetEvent(new GhostingMessageEvent(GhostAlwaysStarting, mGhostingSequence, ghostAlwaysSet->size()));
for(j = mGhostZeroUpdateIndex - 1; j >= 0; j--)
{
AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "Non-scope always in the scope always list.")
// we may end up resending state here, but at least initial state
// will not be resent.
mGhostArray[j]->updateMask = 0;
ghostPushToZero(mGhostArray[j]);
mGhostArray[j]->flags &= ~GhostInfo::NotYetGhosted;
mGhostArray[j]->flags |= GhostInfo::ScopedEvent;
postNetEvent(new GhostAlwaysObjectEvent(mGhostArray[j]->obj, mGhostArray[j]->index));
}
postNetEvent(new GhostingMessageEvent(GhostAlwaysDone, mGhostingSequence));
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
}
void NetConnection::clearGhostInfo()
{
// gotta clear out the ghosts...
for(PacketNotify *walk = mNotifyQueueHead; walk; walk = walk->nextPacket)
{
ghostPacketReceived(walk);
walk->ghostList = NULL;
}
for(S32 i = 0; i < MaxGhostCount; i++)
{
if(mGhostRefs[i].arrayIndex < mGhostFreeIndex)
{
detachObject(&mGhostRefs[i]);
freeGhostInfo(&mGhostRefs[i]);
}
}
AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Invalid indices.");
}
void NetConnection::resetGhosting()
{
if(!mGhostFrom)
return;
// stop all ghosting activity
// send a message to the other side notifying of this
mGhosting = false;
mScoping = false;
postNetEvent(new GhostingMessageEvent(EndGhosting, mGhostingSequence));
mGhostingSequence++;
clearGhostInfo();
//AssertFatal(validateGhostArray(), "Invalid ghost array!");
}
void NetConnection::setGhostAlwaysObject(NetObject *object, U32 index)
{
if(!mGhostTo)
{
object->deleteObject();
setLastError("Invalid packet.");
return;
}
AssertFatal(mLocalGhosts[index] == NULL, "Ghost already in table!");
object->mNetFlags = NetObject::IsGhost;
if(!object->registerObject())
{
if(!mErrorBuffer[0])
setLastError("Invalid packet.");
return;
}
addObject(object);
mLocalGhosts[index] = object;
}
//-----------------------------------------------------------------------------
NetObject *NetConnection::resolveGhost(S32 id)
{
return mLocalGhosts[id];
}
NetObject *NetConnection::resolveGhostParent(S32 id)
{
return mGhostRefs[id].obj;
}
S32 NetConnection::getGhostIndex(NetObject *obj)
{
if(!mGhostFrom)
return obj->mNetIndex;
S32 index = obj->getId() & (GhostLookupTableSize - 1);
for(GhostInfo *gptr = mGhostLookupTable[index]; gptr; gptr = gptr->nextLookupInfo)
{
if(gptr->obj == obj && (gptr->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting | GhostInfo::NotYetGhosted | GhostInfo::KillGhost)) == 0)
return gptr->index;
}
return -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void NetConnection::ghostWriteStartBlock(ResizeBitStream *stream)
{
stream->write(mGhostingSequence);
for(U32 i = 0; i < MaxGhostCount; i++)
{
if(mLocalGhosts[i])
{
stream->writeFlag(true);
stream->writeInt(i, 10);
stream->writeInt(mLocalGhosts[i]->getClassId() - NetObjectClassFirst, NetObjectClassBitSize);
mLocalGhosts[i]->packUpdate(this, 0xFFFFFFFF, stream);
stream->validate();
}
}
stream->writeFlag(false);
}
void NetConnection::ghostReadStartBlock(BitStream *stream)
{
stream->read(&mGhostingSequence);
while(stream->readFlag())
{
U32 index = stream->readInt(10);
U32 tag = (U32) stream->readInt(NetObjectClassBitSize) + NetObjectClassFirst;
NetObject *obj = (NetObject *) ConsoleObject::create(tag);
if(!obj)
{
setLastError("Invalid packet.");
return;
}
obj->mNetFlags = NetObject::IsGhost;
obj->mNetIndex = index;
mLocalGhosts[index] = obj;
mLocalGhosts[index]->unpackUpdate(this, stream);
if(!obj->registerObject())
{
if(mErrorBuffer[0])
setLastError("Invalid packet.");
return;
}
addObject(obj);
}
}

249
sim/netObject.cc Normal file
View file

@ -0,0 +1,249 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/simBase.h"
#include "core/dnet.h"
#include "sim/netConnection.h"
#include "sim/netObject.h"
#include "console/consoleTypes.h"
IMPLEMENT_CONOBJECT(NetObject);
//----------------------------------------------------------------------------
NetObject *NetObject::mDirtyList = NULL;
NetObject::NetObject()
{
// netFlags will clear itself to 0
mNetIndex = U32(-1);
mFirstObjectRef = NULL;
mPrevDirtyList = NULL;
mNextDirtyList = NULL;
mDirtyMaskBits = 0;
}
NetObject::~NetObject()
{
if(mDirtyMaskBits)
{
if(mPrevDirtyList)
mPrevDirtyList->mNextDirtyList = mNextDirtyList;
else
mDirtyList = mNextDirtyList;
if(mNextDirtyList)
mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
}
}
void NetObject::setMaskBits(U32 orMask)
{
AssertFatal(orMask != 0, "Invalid net mask bits set.");
AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
if(!mDirtyMaskBits)
{
AssertFatal(mNextDirtyList == NULL && mPrevDirtyList == NULL, "Object with zero mask already in list.");
if(mDirtyList)
{
mNextDirtyList = mDirtyList;
mDirtyList->mPrevDirtyList = this;
}
mDirtyList = this;
}
mDirtyMaskBits |= orMask;
AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
}
void NetObject::clearMaskBits(U32 orMask)
{
if(isDeleted())
return;
if(mDirtyMaskBits)
{
mDirtyMaskBits &= ~orMask;
if(!mDirtyMaskBits)
{
if(mPrevDirtyList)
mPrevDirtyList->mNextDirtyList = mNextDirtyList;
else
mDirtyList = mNextDirtyList;
if(mNextDirtyList)
mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
mNextDirtyList = mPrevDirtyList = NULL;
}
}
for(GhostInfo *walk = mFirstObjectRef; walk; walk = walk->nextObjectRef)
{
if(walk->updateMask && walk->updateMask == orMask)
{
walk->updateMask = 0;
walk->connection->ghostPushToZero(walk);
}
else
walk->updateMask &= ~orMask;
}
}
void NetObject::collapseDirtyList()
{
Vector<NetObject *> tempV;
for(NetObject *t = mDirtyList; t; t = t->mNextDirtyList)
tempV.push_back(t);
for(NetObject *obj = mDirtyList; obj; )
{
NetObject *next = obj->mNextDirtyList;
U32 orMask = obj->mDirtyMaskBits;
obj->mNextDirtyList = NULL;
obj->mPrevDirtyList = NULL;
obj->mDirtyMaskBits = 0;
if(!obj->isDeleted() && orMask)
{
for(GhostInfo *walk = obj->mFirstObjectRef; walk; walk = walk->nextObjectRef)
{
if(!walk->updateMask)
{
walk->updateMask = orMask;
walk->connection->ghostPushNonZero(walk);
}
else
walk->updateMask |= orMask;
}
}
obj = next;
}
mDirtyList = NULL;
for(U32 i = 0; i < tempV.size(); i++)
{
AssertFatal(tempV[i]->mNextDirtyList == NULL && tempV[i]->mPrevDirtyList == NULL && tempV[i]->mDirtyMaskBits == 0, "Error in collapse");
}
}
//-----------------------------------------------------------------------------
ConsoleMethod(NetObject,scopeToClient,void,3,3,"obj.scopeToClient(%client)")
{
argc;
NetConnection *conn;
if(!Sim::findObject(argv[2], conn))
{
Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", argv[2]);
return;
}
conn->objectLocalScopeAlways((NetObject *) object);
}
ConsoleMethod(NetObject,clearScopeToClient,void,3,3,"obj.clearScopeToClient(%client)")
{
argc;
NetConnection *conn;
if(!Sim::findObject(argv[2], conn))
{
Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", argv[2]);
return;
}
conn->objectLocalClearAlways((NetObject *) object);
}
ConsoleMethod(NetObject,setScopeAlways,void,2,2,"obj.scopeAlways();")
{
argc; argv;
((NetObject *) object)->setScopeAlways();
}
void NetObject::setScopeAlways()
{
if(mNetFlags.test(Ghostable) && !mNetFlags.test(IsGhost))
{
mNetFlags.set(ScopeAlways);
// if it's a ghost always object, add it to the ghost always set
// for ClientReps created later.
Sim::getGhostAlwaysSet()->addObject(this);
// add it to all Connections that already exist.
SimGroup *clientGroup = Sim::getClientGroup();
SimGroup::iterator i;
for(i = clientGroup->begin(); i != clientGroup->end(); i++)
{
NetConnection *con = (NetConnection *) (*i);
if(con->isGhosting())
con->objectInScope(this);
}
}
}
bool NetObject::onAdd()
{
if(mNetFlags.test(ScopeAlways))
setScopeAlways();
return Parent::onAdd();
}
void NetObject::onRemove()
{
while(mFirstObjectRef)
mFirstObjectRef->connection->detachObject(mFirstObjectRef);
Parent::onRemove();
}
//-----------------------------------------------------------------------------
F32 NetObject::getUpdatePriority(CameraScopeQuery*, U32, S32 updateSkips)
{
return F32(updateSkips) * 0.1;
//return 0;
}
U32 NetObject::packUpdate(NetConnection*, U32, BitStream*)
{
return 0;
}
void NetObject::unpackUpdate(NetConnection*, BitStream*)
{
}
void NetObject::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery* /*camInfo*/)
{
// default behavior -
// ghost everything that is ghostable
for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj)
{
NetObject* nobj = dynamic_cast<NetObject*>(*obj);
if (nobj)
{
// Some objects don't ever want to be ghosted
if (!nobj->mNetFlags.test(NetObject::Ghostable))
continue;
if (!nobj->mNetFlags.test(NetObject::ScopeAlways))
{
// it's in scope...
cr->objectInScope(nobj);
}
}
}
}
//-----------------------------------------------------------------------------
void NetObject::initPersistFields()
{
Parent::initPersistFields();
}
void NetObject::consoleInit()
{
}

143
sim/netObject.h Normal file
View file

@ -0,0 +1,143 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _NETOBJECT_H_
#define _NETOBJECT_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _MMATH_H_
#include "Math/mMath.h"
#endif
//-----------------------------------------------------------------------------
class NetConnection;
class NetObject;
//-----------------------------------------------------------------------------
struct CameraScopeQuery
{
NetObject *camera;
Point3F pos; // Pos in world space
Point3F orientation; // Vector in world space
F32 fov; // viwing angle/2
F32 sinFov; // sin(fov/2);
F32 cosFov; // cos(fov/2);
F32 visibleDistance;
};
struct GhostInfo;
//-----------------------------------------------------------------------------
class NetObject: public SimObject
{
// The Ghost Manager needs read/write access
friend class NetConnection;
friend struct GhostInfo;
friend class ProcessList;
// Not the best way to do this, but the event needs access to mNetFlags
friend class GhostAlwaysObjectEvent;
private:
typedef SimObject Parent;
NetObject *mPrevDirtyList;
NetObject *mNextDirtyList;
U32 mDirtyMaskBits;
static NetObject *mDirtyList;
protected:
SimObjectPtr<NetObject> mServerObject;
enum NetFlag
{
IsGhost = BIT(1), // This is a ghost
ScopeAlways = BIT(6), // if set, object always ghosts to clientReps
ScopeLocal = BIT(7), // Ghost only to local client 2
Ghostable = BIT(8), // new flag -- set if this object CAN ghost
MaxNetFlagBit = 15
};
BitSet32 mNetFlags;
U32 mNetIndex; // the index of this ghost in the GhostManager on the server
GhostInfo *mFirstObjectRef;
public:
NetObject();
~NetObject();
// Base class provides IO for it's members but is
// not declared as persitent.
DECLARE_CONOBJECT(NetObject);
static void initPersistFields();
static void consoleInit();
static void collapseDirtyList();
bool onAdd();
void onRemove();
void setMaskBits(U32 orMask);
void clearMaskBits(U32 orMask);
void setScopeAlways();
virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);
virtual U32 packUpdate(NetConnection *, U32 mask, BitStream *stream);
virtual void unpackUpdate(NetConnection *, BitStream *stream);
virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);
U32 getNetIndex() { return mNetIndex; }
bool isServerObject() const;
bool isClientObject() const;
bool isGhost() const;
bool isScopeLocal() const;
bool isScopeable() const;
bool isGhostAlways() const;
};
//-----------------------------------------------------------------------------
inline bool NetObject::isGhost() const
{
return mNetFlags.test(IsGhost);
}
inline bool NetObject::isClientObject() const
{
return mNetFlags.test(IsGhost);
}
inline bool NetObject::isServerObject() const
{
return !mNetFlags.test(IsGhost);
}
inline bool NetObject::isScopeLocal() const
{
return mNetFlags.test(ScopeLocal);
}
inline bool NetObject::isScopeable() const
{
return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways);
}
inline bool NetObject::isGhostAlways() const
{
AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false,
"That's strange, a ScopeAlways non-ghostable object? Something wrong here");
return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways);
}
#endif

218
sim/netStringTable.cc Normal file
View file

@ -0,0 +1,218 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dnet.h"
#include "console/simBase.h"
#include "sim/netConnection.h"
#include "sim/netStringTable.h"
#include "core/stringTable.h"
NetStringTable *gNetStringTable = NULL;
NetStringTable::NetStringTable()
{
firstFree = 1;
firstValid = 1;
for(U32 i = 0; i < MaxStrings; i++)
{
table[i].next = i + 1;
table[i].refCount = 0;
}
for(U32 j = 0; j < HashTableSize; j++)
hashTable[j] = 0;
allocator = new DataChunker(DataChunkerSize);
}
NetStringTable::~NetStringTable()
{
delete allocator;
}
void NetStringTable::clearSentBit(U32 id)
{
// a string has been added - loop through the connections
// and mark it as not yet sent.
for(NetConnection *conn = NetConnection::getConnectionList(); conn; conn = conn->getNext())
conn->clearString(id);
}
void NetStringTable::incStringRef(U32 id)
{
AssertFatal(table[id].refCount != 0, "Cannot inc ref count from zero.");
table[id].refCount++;
}
U32 NetStringTable::addString(const char *string)
{
U32 hash = _StringTable::hashString(string);
U32 bucket = hash % HashTableSize;
for(U16 walk = hashTable[bucket];walk; walk = table[walk].next)
{
if(!dStrcmp(table[walk].string, string))
{
table[walk].refCount++;
return walk;
}
}
U16 e = firstFree;
firstFree = table[e].next;
table[e].refCount++;
table[e].string = (char *) allocator->alloc(dStrlen(string) + 1);
dStrcpy(table[e].string, string);
table[e].next = hashTable[bucket];
hashTable[bucket] = e;
table[e].link = firstValid;
table[firstValid].prevLink = e;
firstValid = e;
table[e].prevLink = 0;
clearSentBit(e);
return e;
}
U32 GameAddTaggedString(const char *string)
{
return gNetStringTable->addString(string);
}
const char *NetStringTable::lookupString(U32 id)
{
if(table[id].refCount == 0)
return NULL;
return table[id].string;
}
void NetStringTable::removeString(U32 id)
{
if(--table[id].refCount)
return;
// unlink first:
U16 prev = table[id].prevLink;
U16 next = table[id].link;
if(next)
table[next].prevLink = prev;
if(prev)
table[prev].link = next;
else
firstValid = next;
// remove it from the hash table
U32 hash = _StringTable::hashString(table[id].string);
U32 bucket = hash % HashTableSize;
for(U16 *walk = &hashTable[bucket];*walk; walk = &table[*walk].next)
{
if(*walk == id)
{
*walk = table[id].next;
break;
}
}
table[id].next = firstFree;
firstFree = id;
}
void NetStringTable::repack()
{
DataChunker *newAllocator = new DataChunker(DataChunkerSize);
for(U16 walk = firstValid; walk; walk = table[walk].link)
{
const char *prevStr = table[walk].string;
table[walk].string = (char *) newAllocator->alloc(dStrlen(prevStr) + 1);
dStrcpy(table[walk].string, prevStr);
}
delete allocator;
allocator = newAllocator;
}
void NetStringTable::create()
{
AssertFatal(gNetStringTable == NULL, "Error, calling NetStringTable::create twice.");
gNetStringTable = new NetStringTable();
}
void NetStringTable::destroy()
{
AssertFatal(gNetStringTable != NULL, "Error, not calling NetStringTable::create.");
delete gNetStringTable;
gNetStringTable = NULL;
}
void NetStringTable::expandString(U32 id, char *buf, U32 bufSize, U32 argc, const char **argv)
{
buf[0] = StringTagPrefixByte;
dSprintf(buf + 1, bufSize - 1, "%d ", id);
const char *string = gNetStringTable->lookupString(id);
if (string != NULL) {
U32 index = dStrlen(buf);
while(index < bufSize)
{
char c = *string++;
if(c == '%')
{
c = *string++;
if(c >= '1' && c <= '9')
{
U32 strIndex = c - '1';
if(strIndex >= argc)
continue;
// start copying out of arg index
const char *copy = argv[strIndex];
// skip past any tags:
if(*copy == StringTagPrefixByte)
{
while(*copy && *copy != ' ')
copy++;
if(*copy)
copy++;
}
while(*copy && index < bufSize)
buf[index++] = *copy++;
continue;
}
}
buf[index++] = c;
if(!c)
break;
}
buf[bufSize - 1] = 0;
} else {
dStrcat(buf, "<NULL>");
}
}
#ifdef DEBUG
void NetStringTable::dumpToConsole()
{
U32 count = 0;
S32 maxIndex = -1;
for ( U32 i = 0; i < MaxStrings; i++ )
{
if ( table[i].refCount > 0 )
{
Con::printf( "%d: \"%c%s%c\" REF: %d", i, 0x10, table[i].string, 0x11, table[i].refCount );
if ( maxIndex == -1 || table[i].refCount > table[maxIndex].refCount )
maxIndex = i;
count++;
}
}
Con::printf( ">> STRINGS: %d MAX REF COUNT: %d \"%c%s%c\" <<",
count,
( maxIndex == -1 ) ? 0 : table[maxIndex].refCount,
0x10,
( maxIndex == -1 ) ? "" : table[maxIndex].string,
0x11 );
}
ConsoleFunction( dumpNetStringTable, void, 1, 1, "dumpNetStringTable()" )
{
argc; argv;
gNetStringTable->dumpToConsole();
}
#endif // DEBUG

67
sim/netStringTable.h Normal file
View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _NETSTRINGTABLE_H_
#define _NETSTRINGTABLE_H_
#ifndef _DATACHUNKER_H_
#include "engine/core/dataChunker.h"
#endif
class NetConnection;
class NetStringTable
{
public:
enum {
MaxStrings = 4096,
HashTableSize = 1287,
StringIdBitSize = 12,
DataChunkerSize = 65536
};
private:
struct Entry
{
char *string;
U16 refCount;
U16 next;
U16 link;
U16 prevLink;
U32 seq;
};
U16 firstFree;
U16 firstValid;
U32 sequenceCount;
Entry table[MaxStrings];
U16 hashTable[HashTableSize];
DataChunker *allocator;
public:
NetStringTable();
~NetStringTable();
void clearSentBit(U32 id);
public:
U32 addString(const char *string);
const char *lookupString(U32 id);
void incStringRef(U32 id);
void removeString(U32 id);
void sendString(U32 id);
void repack();
static void create();
static void destroy();
static void expandString(U32 id, char *buf, U32 bufSize, U32 argc, const char **argv);
#ifdef DEBUG
void dumpToConsole();
#endif // DEBUG
};
extern NetStringTable *gNetStringTable;
#endif

406
sim/pathManager.cc Normal file
View file

@ -0,0 +1,406 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Sim/pathManager.h"
#include "Sim/netConnection.h"
#include "Core/bitStream.h"
#include "interior/interiorInstance.h"
#include "Math/mathIO.h"
namespace {
U32 countNumBits(U32 n)
{
U32 count = 0;
while (n != 0) {
n >>= 1;
count++;
}
return count ? count : 1;
}
} // namespace {}
//--------------------------------------------------------------------------
//-------------------------------------- PathManagerEvent
//
class PathManagerEvent : public NetEvent
{
public:
enum MessageType {
NewPaths,
ModifyPath
};
MessageType message;
U32 modifiedPath;
Vector<PathManager::PathEntry*> mPaths;
public:
PathManagerEvent() {
VECTOR_SET_ASSOCIATION(mPaths);
}
void pack(NetConnection*, BitStream*);
void write(NetConnection*, BitStream*);
void unpack(NetConnection*, BitStream*);
void process(NetConnection*);
DECLARE_CONOBJECT(PathManagerEvent);
};
void PathManagerEvent::pack(NetConnection*, BitStream* stream)
{
AssertFatal(mPaths.size() == 0, "Hm, bad");
if (stream->writeFlag(message == NewPaths) == true) {
// Write out all the new paths...
stream->write(gServerPathManager->mPaths.size());
for (U32 i = 0; i < gServerPathManager->mPaths.size(); i++) {
stream->write(gServerPathManager->mPaths[i]->totalTime);
stream->write(gServerPathManager->mPaths[i]->positions.size());
for (U32 j = 0; j < gServerPathManager->mPaths[i]->positions.size(); j++) {
mathWrite(*stream, gServerPathManager->mPaths[i]->positions[j]);
stream->write(gServerPathManager->mPaths[i]->msToNext[j]);
}
}
} else {
// Write out the modified path...
stream->write(modifiedPath);
stream->write(gServerPathManager->mPaths[modifiedPath]->totalTime);
stream->write(gServerPathManager->mPaths[modifiedPath]->positions.size());
for (U32 j = 0; j < gServerPathManager->mPaths[modifiedPath]->positions.size(); j++) {
mathWrite(*stream, gServerPathManager->mPaths[modifiedPath]->positions[j]);
stream->write(gServerPathManager->mPaths[modifiedPath]->msToNext[j]);
}
}
}
void PathManagerEvent::write(NetConnection*, BitStream *stream)
{
if (stream->writeFlag(message == NewPaths) == true) {
// Write out all the new paths...
stream->write(mPaths.size());
for (U32 i = 0; i < mPaths.size(); i++) {
stream->write(mPaths[i]->totalTime);
stream->write(mPaths[i]->positions.size());
for (U32 j = 0; j < mPaths[i]->positions.size(); j++) {
mathWrite(*stream, mPaths[i]->positions[j]);
stream->write(mPaths[i]->msToNext[j]);
}
}
} else {
// Write out the modified path...
stream->write(modifiedPath);
stream->write(mPaths[0]->totalTime);
stream->write(mPaths[0]->positions.size());
for (U32 j = 0; j < mPaths[0]->positions.size(); j++) {
mathWrite(*stream, mPaths[0]->positions[j]);
stream->write(mPaths[0]->msToNext[j]);
}
}
}
void PathManagerEvent::unpack(NetConnection*, BitStream* stream)
{
AssertFatal(mPaths.size() == 0, "Hm, bad");
message = stream->readFlag() ? NewPaths : ModifyPath;
if (message == NewPaths) {
// Read in all the paths
U32 numPaths, i;
stream->read(&numPaths);
mPaths.setSize(numPaths);
for (i = 0; i < mPaths.size(); i++)
mPaths[i] = new PathManager::PathEntry;
for (i = 0; i < mPaths.size(); i++) {
PathManager::PathEntry& rEntry = *(mPaths[i]);
stream->read(&rEntry.totalTime);
U32 numPoints;
stream->read(&numPoints);
rEntry.positions.setSize(numPoints);
rEntry.msToNext.setSize(numPoints);
for (U32 j = 0; j < rEntry.positions.size(); j++) {
mathRead(*stream, &rEntry.positions[j]);
stream->read(&rEntry.msToNext[j]);
}
}
} else {
// Read in the modified path...
stream->read(&modifiedPath);
AssertFatal(modifiedPath <= gClientPathManager->mPaths.size(), "Error out of bounds path!");
mPaths.push_back(new PathManager::PathEntry);
PathManager::PathEntry& rEntry = *(mPaths[0]);
stream->read(&rEntry.totalTime);
U32 numPoints;
stream->read(&numPoints);
rEntry.positions.setSize(numPoints);
rEntry.msToNext.setSize(numPoints);
for (U32 j = 0; j < rEntry.positions.size(); j++) {
mathRead(*stream, &rEntry.positions[j]);
stream->read(&rEntry.msToNext[j]);
}
}
}
void PathManagerEvent::process(NetConnection*)
{
if (message == NewPaths) {
// Destroy all previous paths...
U32 i;
for (i = 0; i < gClientPathManager->mPaths.size(); i++)
delete gClientPathManager->mPaths[i];
gClientPathManager->mPaths.setSize(mPaths.size());
for (i = 0; i < mPaths.size(); i++) {
gClientPathManager->mPaths[i] = mPaths[i];
mPaths[i] = NULL;
}
} else {
if (modifiedPath == gClientPathManager->mPaths.size()) {
gClientPathManager->mPaths.push_back(mPaths[0]);
mPaths[0] = NULL;
} else {
delete gClientPathManager->mPaths[modifiedPath];
gClientPathManager->mPaths[modifiedPath] = mPaths[0];
mPaths[0] = NULL;
}
}
}
IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent);
//--------------------------------------------------------------------------
//-------------------------------------- PathManager Implementation
//
PathManager* gClientPathManager = NULL;
PathManager* gServerPathManager = NULL;
//--------------------------------------------------------------------------
PathManager::PathManager(const bool isServer)
{
VECTOR_SET_ASSOCIATION(mPaths);
mIsServer = isServer;
}
PathManager::~PathManager()
{
for (U32 i = 0; i < mPaths.size(); i++)
delete mPaths[i];
}
void PathManager::init()
{
AssertFatal(gClientPathManager == NULL && gServerPathManager == NULL, "Error, already initialized the path manager!");
gClientPathManager = new PathManager(false);
gServerPathManager = new PathManager(true);
}
void PathManager::destroy()
{
AssertFatal(gClientPathManager != NULL && gServerPathManager != NULL, "Error, path manager not initialized!");
delete gClientPathManager;
gClientPathManager = NULL;
delete gServerPathManager;
gServerPathManager = NULL;
}
//--------------------------------------------------------------------------
U32 PathManager::allocatePathId()
{
mPaths.increment();
mPaths.last() = new PathEntry;
return (mPaths.size() - 1);
}
void PathManager::updatePath(const U32 id,
const Vector<Point3F>& positions,
const Vector<U32>& times)
{
AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side");
AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range");
AssertFatal(positions.size() == times.size(), "Error, times and positions must match!");
PathEntry& rEntry = *mPaths[id];
rEntry.positions = positions;
rEntry.msToNext = times;
rEntry.totalTime = 0;
for (S32 i = 0; i < S32(rEntry.msToNext.size()) - 1; i++)
rEntry.totalTime += rEntry.msToNext[i];
transmitPath(id);
}
//--------------------------------------------------------------------------
void PathManager::transmitPaths(NetConnection* nc)
{
AssertFatal(mIsServer, "Error, cannot call transmitPaths on client path manager!");
// Send over paths
PathManagerEvent* event = new PathManagerEvent;
event->message = PathManagerEvent::NewPaths;
nc->postNetEvent(event);
}
void PathManager::transmitPath(const U32 id)
{
AssertFatal(mIsServer, "Error, cannot call transmitNewPath on client path manager!");
// Post to all active clients that have already received their paths...
//
SimGroup* pClientGroup = Sim::getClientGroup();
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
NetConnection* nc = dynamic_cast<NetConnection*>(*itr);
if (nc && nc->missionPathsSent()) {
// Transmit the updated path...
PathManagerEvent* event = new PathManagerEvent;
event->message = PathManagerEvent::ModifyPath;
event->modifiedPath = id;
nc->postNetEvent(event);
}
}
}
void PathManager::getPathPosition(const U32 id,
const U32 msPosition,
Point3F& rPosition)
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
// Ok, query holds our path information...
U32 ms = msPosition;
if (ms > mPaths[id]->totalTime)
ms = mPaths[id]->totalTime;
U32 startNode = 0;
while (ms > mPaths[id]->msToNext[startNode]) {
ms -= mPaths[id]->msToNext[startNode];
startNode++;
}
U32 endNode = (startNode + 1) % mPaths[id]->positions.size();
Point3F& rStart = mPaths[id]->positions[startNode];
Point3F& rEnd = mPaths[id]->positions[endNode];
F32 interp = F32(ms) / F32(mPaths[id]->msToNext[startNode]);
rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
}
U32 PathManager::getPathTotalTime(const U32 id) const
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
return mPaths[id]->totalTime;
}
U32 PathManager::getPathNumWaypoints(const U32 id) const
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
return mPaths[id]->positions.size();
}
U32 PathManager::getWaypointTime(const U32 id, const U32 wayPoint) const
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
AssertFatal(wayPoint < getPathNumWaypoints(id), "Invalid waypoint!");
U32 time = 0;
for (U32 i = 0; i < wayPoint; i++)
time += mPaths[id]->msToNext[i];
return time;
}
U32 PathManager::getPathTimeBits(const U32 id)
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
return countNumBits(mPaths[id]->totalTime);
}
U32 PathManager::getPathWaypointBits(const U32 id)
{
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
return countNumBits(mPaths[id]->positions.size());
}
bool PathManager::dumpState(BitStream* stream) const
{
stream->write(mPaths.size());
for (U32 i = 0; i < mPaths.size(); i++) {
const PathEntry& rEntry = *mPaths[i];
stream->write(rEntry.totalTime);
stream->write(rEntry.positions.size());
for (U32 j = 0; j < rEntry.positions.size(); j++) {
mathWrite(*stream, rEntry.positions[j]);
stream->write(rEntry.msToNext[j]);
}
}
return stream->getStatus() == Stream::Ok;
}
bool PathManager::readState(BitStream* stream)
{
U32 i;
for (i = 0; i < mPaths.size(); i++)
delete mPaths[i];
U32 numPaths;
stream->read(&numPaths);
mPaths.setSize(numPaths);
for (i = 0; i < mPaths.size(); i++) {
mPaths[i] = new PathEntry;
PathEntry& rEntry = *mPaths[i];
stream->read(&rEntry.totalTime);
U32 numPositions;
stream->read(&numPositions);
rEntry.positions.setSize(numPositions);
rEntry.msToNext.setSize(numPositions);
for (U32 j = 0; j < rEntry.positions.size(); j++) {
mathRead(*stream, &rEntry.positions[j]);
stream->read(&rEntry.msToNext[j]);
}
}
return stream->getStatus() == Stream::Ok;
}

105
sim/pathManager.h Normal file
View file

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _PATHMANAGER_H_
#define _PATHMANAGER_H_
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "Core/tVector.h"
#endif
#ifndef _MPOINT_H_
#include "Math/mPoint.h"
#endif
#ifndef _MQUAT_H_
#include "Math/mQuat.h"
#endif
class NetConnection;
class BitStream;
class PathManager
{
friend class PathManagerEvent;
private:
struct PathEntry {
U32 totalTime;
Vector<Point3F> positions;
Vector<U32> msToNext;
PathEntry() {
VECTOR_SET_ASSOCIATION(positions);
VECTOR_SET_ASSOCIATION(msToNext);
}
};
Vector<PathEntry*> mPaths;
public:
enum PathType {
BackAndForth,
Looping
};
public:
PathManager(const bool isServer);
~PathManager();
static void init();
static void destroy();
//-------------------------------------- Path querying
public:
bool isValidPath(const U32 id) const;
void getPathPosition(const U32 id, const U32 msPosition, Point3F& rPosition);
U32 getPathTotalTime(const U32 id) const;
U32 getPathNumWaypoints(const U32 id) const;
U32 getWaypointTime(const U32 id, const U32 wayPoint) const;
U32 getPathTimeBits(const U32 id);
U32 getPathWaypointBits(const U32 id);
//-------------------------------------- Path Registration/Transmission/Management
public:
// Called after mission load to clear out the paths on the client, and to transmit
// the information for the current mission's paths.
void transmitPaths(NetConnection*);
void transmitPath(U32);
U32 allocatePathId();
void updatePath(const U32 id, const Vector<Point3F>&, const Vector<U32>&);
//-------------------------------------- State dumping/reading
public:
bool dumpState(BitStream*) const;
bool readState(BitStream*);
private:
bool mIsServer;
bool mPathsSent;
};
struct PathNode {
Point3F position;
QuatF rotation;
U32 msToNext;
};
extern PathManager* gClientPathManager;
extern PathManager* gServerPathManager;
//--------------------------------------------------------------------------
inline bool PathManager::isValidPath(const U32 id) const
{
return (id < mPaths.size()) && mPaths[id]->positions.size() > 0;
}
#endif // _H_PATHMANAGER

2071
sim/sceneObject.cc Normal file

File diff suppressed because it is too large Load diff

502
sim/sceneObject.h Normal file
View file

@ -0,0 +1,502 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _SCENEOBJECT_H_
#define _SCENEOBJECT_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _NETOBJECT_H_
#include "sim/netObject.h"
#endif
#ifndef _COLLISION_H_
#include "collision/collision.h"
#endif
#ifndef _POLYHEDRON_H_
#include "collision/polyhedron.h"
#endif
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
#ifndef _OBJECTTYPES_H_
#include "game/objectTypes.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "scenegraph/lightManager.h"
#endif
//-------------------------------------- Forward declarations...
class SceneObject;
class SceneGraph;
class SceneState;
class SceneRenderImage;
class Box3F;
class Point3F;
class LightManager;
class Convex;
//----------------------------------------------------------------------------
struct RayInfo: public Collision {
// The collision struct has object, point, normal & material.
F32 t;
};
//--------------------------------------------------------------------------
// There are two indiscretions here. First is the name, which refers rather
// blatantly to the container bin system. A hygine issue. Next is the
// user defined U32, which is added solely for the zoning system. This should
// properly be split up into two structures, for the disparate purposes, especially
// since it's not nice to force the container bin to use 20 bytes structures when
// it could get away with a 16 byte version.
class SceneObjectRef
{
public:
SceneObject* object;
SceneObjectRef* nextInBin;
SceneObjectRef* prevInBin;
SceneObjectRef* nextInObj;
U32 zone;
};
// A scope frustum describes a pyramid to clip new portals against. It is
// rooted at the root position of the scoping query, which is not stored
// here.
class ScopeFrustum
{
public:
enum Constants {
TopLeft = 0,
TopRight = 1,
BottomLeft = 2,
BottomRight = 3
};
Point3F frustumPoints[4];
};
//----------------------------------------------------------------------------
class Container
{
public:
struct Link
{
Link* next;
Link* prev;
Link();
void unlink();
void linkAfter(Link* ptr);
};
struct CallbackInfo {
AbstractPolyList* polyList;
Box3F boundingBox;
SphereF boundingSphere;
S32 key;
};
static const U32 csmNumBins;
static const F32 csmBinSize;
static const F32 csmTotalBinSize;
static const U32 csmRefPoolBlockSize;
static U32 smCurrSeqKey;
private:
Link mStart,mEnd;
SceneObjectRef* mFreeRefPool;
Vector<SceneObjectRef*> mRefPoolBlocks;
SceneObjectRef* mBinArray;
SceneObjectRef mOverflowBin;
public:
Container();
~Container();
// Basic database operations
typedef void (*FindCallback)(SceneObject*,S32 key);
void findObjects(U32 mask, FindCallback, S32 key = 0);
void findObjects(const Box3F& box, U32 mask, FindCallback, S32 key = 0);
void polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask,
FindCallback, S32 key = 0);
// Line intersection
bool castRay(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info);
bool collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info);
// Poly list
bool buildPolyList(const Box3F& box, U32 mask, AbstractPolyList*,FindCallback=0,S32 key = 0);
bool buildCollisionList(const Box3F& box, const Point3F& start, const Point3F& end, const VectorF& velocity,
U32 mask,CollisionList* collisionList,FindCallback = 0,S32 key = 0,const Box3F *queryExpansion = 0);
bool buildCollisionList(const Polyhedron& polyhedron,
const Point3F& start, const Point3F& end,
const VectorF& velocity,
U32 mask, CollisionList* collisionList,
FindCallback callback = 0, S32 key = 0);
//
bool addObject(SceneObject*);
bool removeObject(SceneObject*);
void addRefPoolBlock();
SceneObjectRef* allocateObjectRef();
void freeObjectRef(SceneObjectRef*);
void insertIntoBins(SceneObject*);
void removeFromBins(SceneObject*);
// Checkbins makes sure that we're not just sticking the object right back
// where it came from. The overloaded insertInto is so we don't calculate
// the ranges twice.
void checkBins(SceneObject*);
void insertIntoBins(SceneObject*, U32, U32, U32, U32);
// Object searches to support console querying of the database. ONLY WORKS ON SERVER
private:
Vector<SimObjectPtr<SceneObject>*> mSearchList;
S32 mCurrSearchPos;
Point3F mSearchReferencePoint;
void cleanupSearchVectors();
public:
void initRadiusSearch(const Point3F& searchPoint,
const F32 searchRadius,
const U32 searchMask);
U32 containerSearchNext();
F32 containerSearchCurrDist();
F32 containerSearchCurrRadDamageDist();
};
//----------------------------------------------------------------------------
// For simple queries. Simply creates a vector of the objects
//
class SimpleQueryList
{
public:
Vector<SceneObject*> mList;
public:
SimpleQueryList() {
VECTOR_SET_ASSOCIATION(mList);
}
void insertObject(SceneObject* obj) { mList.push_back(obj); }
static void insertionCallback(SceneObject* obj, S32 key);
};
//----------------------------------------------------------------------------
class SceneObject : public NetObject, public Container::Link
{
typedef NetObject Parent;
friend class Container;
friend class SceneGraph;
friend class SceneState;
//-------------------------------------- Public constants
public:
enum {
MaxObjectZones = 128
};
enum TraverseColor {
White = 0,
Grey = 1,
Black = 2
};
//-------------------------------------- Public interfaces
// C'tors and D'tors
private:
SceneObject(const SceneObject&); // disallowed
public:
SceneObject();
virtual ~SceneObject();
// Scripting interface
const char* scriptThis();
// Collision and transform related interface
public:
virtual void disableCollision();
virtual void enableCollision();
bool isCollisionEnabled() const { return mCollisionCount == 0; }
virtual bool isDisplacable() const;
virtual Point3F getMomentum() const;
virtual void setMomentum(const Point3F&);
virtual F32 getMass() const;
virtual bool displaceObject(const Point3F& displaceVector);
const MatrixF& getTransform() const { return mObjToWorld; }
const MatrixF& getWorldTransform() const { return mWorldToObj; }
const VectorF& getScale() const { return mObjScale; }
const Box3F& getObjBox() const { return mObjBox; }
const Box3F& getWorldBox() const { return mWorldBox; }
const SphereF& getWorldSphere() const { return mWorldSphere; }
Point3F getBoxCenter() const { return (mWorldBox.min + mWorldBox.max) * 0.5f; }
virtual void setTransform(const MatrixF&);
virtual void setScale(const VectorF & scale);
virtual void setRenderTransform(const MatrixF&);
const MatrixF& getRenderTransform() const { return mRenderObjToWorld; }
const MatrixF& getRenderWorldTransform() const { return mRenderWorldToObj; }
const Box3F& getRenderWorldBox() const { return mRenderWorldBox; }
virtual void buildConvex(const Box3F& box,Convex* convex);
virtual bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
virtual BSPNode* buildCollisionBSP(BSPTree *tree, const Box3F &box, const SphereF &sphere);
virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
virtual bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info);
Point3F getPosition() const;
Point3F getRenderPosition() const;
void setPosition(const Point3F &pos);
// Rendering and zone interface
public:
bool isManagingZones() const;
U32 getZoneRangeStart() const { return mZoneRangeStart; }
U32 getNumCurrZones() const { return mNumCurrZones; }
U32 getCurrZone(const U32 index) const;
virtual bool getOverlappingZones(SceneObject* obj, U32* zones, U32* numZones);
virtual U32 getPointZone(const Point3F& p);
virtual void renderObject(SceneState*, SceneRenderImage*);
virtual bool prepRenderImage(SceneState*, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState = false);
virtual bool scopeObject(const Point3F& rootPosition,
const F32 rootDistance,
bool* zoneScopeState);
void addToScene();
void removeFromScene();
//-------------------------------------- Derived class interface
// Overrides
protected:
bool onAdd();
void onRemove();
void inspectPostApply();
// Overrideables
protected:
virtual bool onSceneAdd(SceneGraph*);
virtual void onSceneRemove();
// Portal functions.
virtual void transformModelview(const U32 portalIndex, const MatrixF& oldMV, MatrixF* newMV);
virtual void transformPosition(const U32 portalIndex, Point3F& point);
virtual bool computeNewFrustum(const U32 portalIndex,
const F64* oldFrustum,
const F64 nearPlane,
const F64 farPlane,
const RectI& oldViewport,
F64* newFrustum,
RectI& newViewport,
const bool flippedMatrix);
virtual void openPortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState);
virtual void closePortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState);
public:
virtual void getWSPortalPlane(const U32 portalIndex, PlaneF*);
protected:
void setLastState(SceneState*, U32);
bool isLastState(SceneState*, U32) const;
void setTraverseColor(TraverseColor);
TraverseColor getTraverseColor() const;
// lighting info:
struct LightingInfo
{
LightingInfo();
bool mUseInfo;
bool mDirty;
ColorF mDefaultColor;
ColorF mAlarmColor;
SimObjectPtr<SceneObject> mInterior;
bool mHasLastColor;
ColorF mLastColor;
U32 mLastTime;
static LightInfo smAmbientLight;
enum {
Interior = 0,
Terrain,
};
U32 mLightSource;
};
virtual void installLights();
virtual void uninstallLights();
virtual bool getLightingAmbientColor(ColorF * col);
LightingInfo mLightingInfo;
// Transform and Collision related members
protected:
Container* mContainer;
MatrixF mObjToWorld; // object transform
MatrixF mWorldToObj; // inverse transform
Point3F mObjScale; // object scale
Box3F mObjBox;
Box3F mWorldBox;
SphereF mWorldSphere;
MatrixF mRenderObjToWorld;
MatrixF mRenderWorldToObj;
Box3F mRenderWorldBox;
SphereF mRenderWorldSphere;
void resetWorldBox();
void resetRenderWorldBox();
SceneObjectRef* mZoneRefHead;
SceneObjectRef* mBinRefHead;
U32 mBinMinX;
U32 mBinMaxX;
U32 mBinMinY;
U32 mBinMaxY;
U32 mContainerSeqKey;
U32 getContainerSeqKey() const { return mContainerSeqKey; }
void setContainerSeqKey(const U32 key) { mContainerSeqKey = key; }
public:
Container* getContainer() { return mContainer; }
protected:
S32 mCollisionCount;
public:
U32 getTypeMask() { return(mTypeMask); }
// Rendering related members
protected:
SceneGraph* mSceneManager;
U32 mZoneRangeStart; // 0xFFFFFFFF == no zones
// U32 mCurrZones[MaxObjectZones];
// SceneObject* mCurrZoneOwners[MaxObjectZones];
U32 mNumCurrZones;
private:
TraverseColor mTraverseColor;
SceneState* mLastState;
U32 mLastStateKey;
// Persist and console
public:
static void initPersistFields();
static void consoleInit();
DECLARE_CONOBJECT(SceneObject);
};
//--------------------------------------------------------------------------
extern Container gServerContainer;
extern Container gClientContainer;
//--------------------------------------------------------------------------
//-------------------------------------- Inlines
//
inline bool SceneObject::isManagingZones() const
{
return mZoneRangeStart != 0xFFFFFFFF;
}
inline void SceneObject::setLastState(SceneState* state, U32 key)
{
mLastState = state;
mLastStateKey = key;
}
inline bool SceneObject::isLastState(SceneState* state, U32 key) const
{
return (mLastState == state && mLastStateKey == key);
}
inline void SceneObject::setTraverseColor(TraverseColor c)
{
mTraverseColor = c;
}
inline SceneObject::TraverseColor SceneObject::getTraverseColor() const
{
return mTraverseColor;
}
inline U32 SceneObject::getCurrZone(const U32 index) const
{
// Not the most efficient way to do this, walking the list,
// but it's an uncommon call...
SceneObjectRef* walk = mZoneRefHead;
for (U32 i = 0; i < index; i++) {
walk = walk->nextInObj;
AssertFatal(walk, "Error, too few object refs!");
}
AssertFatal(walk, "Error, too few object refs!");
return walk->zone;
}
//--------------------------------------------------------------------------
inline SceneObjectRef* Container::allocateObjectRef()
{
if (mFreeRefPool == NULL) {
addRefPoolBlock();
}
AssertFatal(mFreeRefPool, "Error, should always have a free reference here!");
SceneObjectRef* ret = mFreeRefPool;
mFreeRefPool = mFreeRefPool->nextInObj;
ret->nextInObj = NULL;
return ret;
}
inline void Container::freeObjectRef(SceneObjectRef* trash)
{
trash->nextInBin = NULL;
trash->prevInBin = NULL;
trash->nextInObj = mFreeRefPool;
mFreeRefPool = trash;
}
inline void Container::findObjects(U32 mask, FindCallback callback, S32 key)
{
for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next) {
SceneObject* ptr = static_cast<SceneObject*>(itr);
if ((ptr->getType() & mask) != 0 && !ptr->mCollisionCount)
(*callback)(ptr,key);
}
}
#endif // _H_SCENEOBJECT_

361
sim/simPath.cc Normal file
View file

@ -0,0 +1,361 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Sim/simPath.h"
#include "console/consoleTypes.h"
#include "Sim/pathManager.h"
#include "PlatformWin32/platformGL.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneState.h"
#include "Math/mathIO.h"
#include "Core/bitStream.h"
//--------------------------------------------------------------------------
//-------------------------------------- Console functions and cmp funcs
//
namespace {
static ColorF cubeColors[8] = {
ColorF(0, 0, 0),
ColorF(1, 0, 0),
ColorF(0, 1, 0),
ColorF(0, 0, 1),
ColorF(1, 1, 0),
ColorF(1, 0, 1),
ColorF(0, 1, 1),
ColorF(1, 1, 1)
};
static Point3F cubePoints[8] = {
Point3F(-1, -1, -1),
Point3F(-1, -1, 1),
Point3F(-1, 1, -1),
Point3F(-1, 1, 1),
Point3F( 1, -1, -1),
Point3F( 1, -1, 1),
Point3F( 1, 1, -1),
Point3F( 1, 1, 1)
};
static U32 cubeFaces[6][4] = {
{ 0, 2, 6, 4 },
{ 0, 2, 3, 1 },
{ 0, 1, 5, 4 },
{ 3, 2, 6, 7 },
{ 7, 6, 4, 5 },
{ 3, 7, 5, 1 }
};
void wireCube(F32 size, Point3F pos)
{
glDisable(GL_CULL_FACE);
for(int i = 0; i < 6; i++)
{
glBegin(GL_LINE_LOOP);
for(int vert = 0; vert < 4; vert++)
{
int idx = cubeFaces[i][vert];
glColor3f(cubeColors[idx].red, cubeColors[idx].green, cubeColors[idx].blue);
glVertex3f(cubePoints[idx].x * size + pos.x, cubePoints[idx].y * size + pos.y, cubePoints[idx].z * size + pos.z);
}
glEnd();
}
}
void cPathMissionLoadDone(SimObject*, S32, const char**)
{
// Need to load subobjects for all loaded interiors...
SimGroup* pMissionGroup = dynamic_cast<SimGroup*>(Sim::findObject("MissionGroup"));
AssertFatal(pMissionGroup != NULL, "Error, mission done loading and no mission group?");
U32 currStart = 0;
U32 currEnd = 1;
Vector<SimGroup*> groups;
groups.push_back(pMissionGroup);
while (true) {
for (U32 i = currStart; i < currEnd; i++) {
for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) {
if (dynamic_cast<SimGroup*>(*itr) != NULL)
groups.push_back(static_cast<SimGroup*>(*itr));
}
}
if (groups.size() == currEnd) {
break;
} else {
currStart = currEnd;
currEnd = groups.size();
}
}
for (U32 i = 0; i < groups.size(); i++) {
Path* pPath = dynamic_cast<Path*>(groups[i]);
if (pPath)
pPath->finishPath();
}
}
S32 FN_CDECL cmpPathObject(const void* p1, const void* p2)
{
SimObject* o1 = *((SimObject**)p1);
SimObject* o2 = *((SimObject**)p2);
Marker* m1 = dynamic_cast<Marker*>(o1);
Marker* m2 = dynamic_cast<Marker*>(o2);
if (m1 == NULL && m2 == NULL)
return 0;
else if (m1 != NULL && m2 == NULL)
return 1;
else if (m1 == NULL && m2 != NULL)
return -1;
else {
// Both markers...
return S32(m1->mSeqNum) - S32(m2->mSeqNum);
}
}
} // namespace {}
//--------------------------------------------------------------------------
//-------------------------------------- Implementation
//
IMPLEMENT_CONOBJECT(Path);
Path::Path()
{
mPathIndex = NoPathIndex;
}
Path::~Path()
{
//
}
//--------------------------------------------------------------------------
void Path::initPersistFields()
{
Parent::initPersistFields();
//
}
void Path::consoleInit()
{
Con::addCommand("pathOnMissionLoadDone", cPathMissionLoadDone, "pathOnMissionLoadDone()", 1, 1);
}
//--------------------------------------------------------------------------
bool Path::onAdd()
{
if(!Parent::onAdd())
return false;
return true;
}
void Path::onRemove()
{
//
Parent::onRemove();
}
//--------------------------------------------------------------------------
void Path::finishPath()
{
// If we need to, allocate a path index from the manager
if (mPathIndex == NoPathIndex)
mPathIndex = gServerPathManager->allocatePathId();
// Sort the markers objects into sequence order
dQsort(objectList.address(), objectList.size(), sizeof(SimObject*), cmpPathObject);
Vector<Point3F> positions;
Vector<U32> times;
for (iterator itr = begin(); itr != end(); itr++) {
Marker* pMarker = dynamic_cast<Marker*>(*itr);
if (pMarker != NULL) {
Point3F pos;
pMarker->getTransform().getColumn(3, &pos);
positions.push_back(pos);
times.push_back(pMarker->mMSToNext);
}
}
// DMMTODO: Looping paths.
gServerPathManager->updatePath(mPathIndex, positions, times);
}
void Path::addObject(SimObject* obj)
{
Parent::addObject(obj);
if (mPathIndex != NoPathIndex) {
// If we're already finished, and this object is a marker, then we need to
// update our path information...
if (dynamic_cast<Marker*>(obj) != NULL)
finishPath();
}
}
void Path::removeObject(SimObject* obj)
{
bool recalc = dynamic_cast<Marker*>(obj) != NULL;
Parent::removeObject(obj);
if (mPathIndex != NoPathIndex && recalc == true)
finishPath();
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1(Marker);
Marker::Marker()
{
// Not ghostable unless we're editing...
//mNetFlags.set(Ghostable);
mTypeMask = MarkerObjectType;
mSeqNum = 0;
mMSToNext = 1000;
}
Marker::~Marker()
{
//
}
//--------------------------------------------------------------------------
void Marker::initPersistFields()
{
Parent::initPersistFields();
addField("seqNum", TypeS32, Offset(mSeqNum, Marker));
addField("msToNext", TypeS32, Offset(mMSToNext, Marker));
}
void Marker::consoleInit()
{
//
}
//--------------------------------------------------------------------------
bool Marker::onAdd()
{
if(!Parent::onAdd())
return false;
mObjBox = Box3F(Point3F(-.25, -.25, -.25), Point3F(.25, .25, .25));
resetWorldBox();
addToScene();
return true;
}
void Marker::onRemove()
{
removeFromScene();
Parent::onRemove();
}
//--------------------------------------------------------------------------
bool Marker::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this)) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
state->insertRenderImage(image);
}
return false;
}
void Marker::renderObject(SceneState* state, SceneRenderImage*)
{
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
// Uncomment this if this is a "simple" (non-zone managing) object
state->setupObjectProjection(this);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
dglMultMatrix(&mObjToWorld);
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
// RENDER CODE HERE
wireCube(0.5, Point3F(0, 0, 0));
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//--------------------------------------------------------------------------
U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
Point3F pos;
getTransform().getColumn(3, &pos);
mathWrite(*stream, pos);
//
return retMask;
}
void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
Point3F pos;
mathRead(*stream, &pos);
MatrixF temp(true);
temp.setColumn(3, pos);
setTransform(temp);
}

89
sim/simPath.h Normal file
View file

@ -0,0 +1,89 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _SIMPATH_H_
#define _SIMPATH_H_
#ifndef _SCENEOBJECT_H_
#include "Sim/sceneObject.h"
#endif
//--------------------------------------------------------------------------
class Path : public SimGroup
{
typedef SimGroup Parent;
public:
enum {
NoPathIndex = 0xFFFFFFFF
};
private:
U32 mPathIndex;
protected:
bool onAdd();
void onRemove();
public:
Path();
~Path();
void addObject(SimObject*);
void removeObject(SimObject*);
void finishPath();
U32 getPathIndex() const;
DECLARE_CONOBJECT(Path);
static void initPersistFields();
static void consoleInit();
};
//--------------------------------------------------------------------------
class Marker : public SceneObject
{
typedef SceneObject Parent;
friend class Path;
public:
U32 mSeqNum;
U32 mMSToNext;
// Rendering
protected:
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
void renderObject(SceneState*, SceneRenderImage*);
protected:
bool onAdd();
void onRemove();
public:
Marker();
~Marker();
DECLARE_CONOBJECT(Marker);
static void initPersistFields();
static void consoleInit();
U32 packUpdate(NetConnection*, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection*, BitStream* stream);
};
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
inline U32 Path::getPathIndex() const
{
return mPathIndex;
}
#endif // _H_PATH