mirror of
https://github.com/tribes2/engine.git
synced 2026-03-08 06:50:31 +00:00
t2 engine svn checkout
This commit is contained in:
commit
ff569bd2ae
988 changed files with 394180 additions and 0 deletions
1637
sim/actionMap.cc
Normal file
1637
sim/actionMap.cc
Normal file
File diff suppressed because it is too large
Load diff
142
sim/actionMap.h
Normal file
142
sim/actionMap.h
Normal 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_
|
||||
72
sim/cannedChatDataBlock.cc
Normal file
72
sim/cannedChatDataBlock.cc
Normal 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
37
sim/cannedChatDataBlock.h
Normal 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
392
sim/decalManager.cc
Normal 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
93
sim/decalManager.h
Normal 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
80
sim/frameAllocator.cc
Normal 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
33
sim/frameAllocator.h
Normal 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
1164
sim/netConnection.cc
Normal file
File diff suppressed because it is too large
Load diff
526
sim/netConnection.h
Normal file
526
sim/netConnection.h
Normal 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
270
sim/netDownload.cc
Normal 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
355
sim/netEvent.cc
Normal 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
908
sim/netGhost.cc
Normal 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
249
sim/netObject.cc
Normal 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
143
sim/netObject.h
Normal 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
218
sim/netStringTable.cc
Normal 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
67
sim/netStringTable.h
Normal 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
406
sim/pathManager.cc
Normal 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
105
sim/pathManager.h
Normal 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
2071
sim/sceneObject.cc
Normal file
File diff suppressed because it is too large
Load diff
502
sim/sceneObject.h
Normal file
502
sim/sceneObject.h
Normal 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
361
sim/simPath.cc
Normal 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
89
sim/simPath.h
Normal 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue