123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, 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"
- #include "console/engineAPI.h"
- #ifdef TORQUE_AFX_ENABLED
- #include "afx/arcaneFX.h"
- #endif
- IMPLEMENT_CONOBJECT(NetObject);
- // More information can be found in the Torque Manual (CHM)
- ConsoleDocClass( NetObject,
- "@brief Superclass for all ghostable networked objects.\n\n"
- "@ingroup Networking\n");
- //----------------------------------------------------------------------------
- NetObject *NetObject::mDirtyList = NULL;
- NetObject::NetObject()
- {
- // netFlags will clear itself to 0
- mNetIndex = U32(-1);
- mFirstObjectRef = NULL;
- mPrevDirtyList = NULL;
- mNextDirtyList = NULL;
- mDirtyMaskBits = 0;
- #ifdef TORQUE_AFX_ENABLED
- mScope_id = 0;
- mScope_refs = 0;
- mScope_registered = false;
- #endif
- }
- NetObject::~NetObject()
- {
- if(mDirtyMaskBits)
- {
- if(mPrevDirtyList)
- mPrevDirtyList->mNextDirtyList = mNextDirtyList;
- else
- mDirtyList = mNextDirtyList;
- if(mNextDirtyList)
- mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
- }
- }
- String NetObject::describeSelf() const
- {
- String desc = Parent::describeSelf();
- if( isClientObject() )
- desc += "|net: client";
- else
- desc += "|net: server";
- return desc;
- }
- 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()
- {
- #ifdef TORQUE_DEBUG
- Vector<NetObject *> tempV;
- for(NetObject *t = mDirtyList; t; t = t->mNextDirtyList)
- tempV.push_back(t);
- #endif
- for(NetObject *obj = mDirtyList; obj; )
- {
- NetObject *next = obj->mNextDirtyList;
- U32 dirtyMask = obj->mDirtyMaskBits;
- obj->mNextDirtyList = NULL;
- obj->mPrevDirtyList = NULL;
- obj->mDirtyMaskBits = 0;
- if(!obj->isDeleted() && dirtyMask)
- {
- for(GhostInfo *walk = obj->mFirstObjectRef; walk; walk = walk->nextObjectRef)
- {
- U32 orMask = obj->filterMaskBits(dirtyMask,walk->connection);
- if(!walk->updateMask && orMask)
- {
- walk->updateMask = orMask;
- walk->connection->ghostPushNonZero(walk);
- }
- else
- walk->updateMask |= orMask;
- }
- }
- obj = next;
- }
- mDirtyList = NULL;
- #ifdef TORQUE_DEBUG
- for(U32 i = 0; i < tempV.size(); i++)
- {
- AssertFatal(tempV[i]->mNextDirtyList == NULL && tempV[i]->mPrevDirtyList == NULL && tempV[i]->mDirtyMaskBits == 0, "Error in collapse");
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( NetObject, scopeToClient, void, ( NetConnection* client),,
- "@brief Cause the NetObject to be forced as scoped on the specified NetConnection.\n\n"
- "@param client The connection this object will always be scoped to\n\n"
- "@tsexample\n"
- "// Called to create new cameras in TorqueScript\n"
- "// %this - The active GameConnection\n"
- "// %spawnPoint - The spawn point location where we creat the camera\n"
- "function GameConnection::spawnCamera(%this, %spawnPoint)\n"
- "{\n"
- " // If this connection's camera exists\n"
- " if(isObject(%this.camera))\n"
- " {\n"
- " // Add it to the mission group to be cleaned up later\n"
- " MissionCleanup.add( %this.camera );\n\n"
- " // Force it to scope to the client side\n"
- " %this.camera.scopeToClient(%this);\n"
- " }\n"
- "}\n"
- "@endtsexample\n\n"
-
- "@see clearScopeToClient()\n")
- {
- if(!client)
- {
- Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", client);
- return;
- }
- client->objectLocalScopeAlways(object);
- }
- //ConsoleMethod(NetObject,scopeToClient,void,3,3,"(NetConnection %client)"
- // "Cause the NetObject to be forced as scoped on the specified NetConnection.")
- //{
- // TORQUE_UNUSED(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(object);
- //}
- DefineEngineMethod( NetObject, clearScopeToClient, void, ( NetConnection* client),,
- "@brief Undo the effects of a scopeToClient() call.\n\n"
- "@param client The connection to remove this object's scoping from \n\n"
-
- "@see scopeToClient()\n")
- {
- if(!client)
- {
- Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", client);
- return;
- }
- client->objectLocalClearAlways(object);
- }
- //ConsoleMethod(NetObject,clearScopeToClient,void,3,3,"clearScopeToClient(%client)"
- // "Undo the effects of a scopeToClient() call.")
- //{
- // TORQUE_UNUSED(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(object);
- //}
- DefineEngineMethod( NetObject, setScopeAlways, void, (),,
- "@brief Always scope this object on all connections.\n\n"
- "The object is marked as ScopeAlways and is immediately ghosted to "
- "all active connections. This function has no effect if the object "
- "is not marked as Ghostable.\n\n")
- {
- object->setScopeAlways();
- }
- //ConsoleMethod(NetObject,setScopeAlways,void,2,2,"Always scope this object on all connections.")
- //{
- // TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
- // 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);
- }
- }
- }
- void NetObject::clearScopeAlways()
- {
- if(!mNetFlags.test(IsGhost))
- {
- mNetFlags.clear(ScopeAlways);
- Sim::getGhostAlwaysSet()->removeObject(this);
- // Un ghost this object from all the connections
- while(mFirstObjectRef)
- mFirstObjectRef->connection->detachObject(mFirstObjectRef);
- }
- }
- 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;
- }
- U32 NetObject::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
- {
- 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)
- {
- AssertFatal(!nobj->mNetFlags.test(NetObject::Ghostable) || !nobj->mNetFlags.test(NetObject::IsGhost),
- "NetObject::onCameraScopeQuery: object marked both ghostable and as ghost");
- // 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();
- }
- DefineEngineMethod( NetObject, getGhostID, S32, (),,
- "@brief Get the ghost index of this object from the server.\n\n"
- "@returns The ghost ID of this NetObject on the server\n"
- "@tsexample\n"
- "%ghostID = LocalClientConnection.getGhostId( %serverObject );\n"
- "@endtsexample\n\n")
- {
- return object->getNetIndex();
- }
- //ConsoleMethod( NetObject, getGhostID, S32, 2, 2, "")
- //{
- // return object->getNetIndex();
- //}
- DefineEngineMethod( NetObject, getClientObject, S32, (),,
- "@brief Returns a pointer to the client object when on a local connection.\n\n"
- "Short-Circuit-Networking: this is only valid for a local-client / singleplayer situation.\n\n"
- "@returns the SimObject ID of the client object.\n"
- "@tsexample\n"
- "// Psuedo-code, some values left out for this example\n"
- "%node = new ParticleEmitterNode(){};\n"
- "%clientObject = %node.getClientObject();\n"
- "if(isObject(%clientObject)\n"
- " %clientObject.setTransform(\"0 0 0\");\n\n"
- "@endtsexample\n\n"
-
- "@see @ref local_connections")
- {
- NetObject *obj = object->getClientObject();
- if ( obj )
- return obj->getId();
-
- return 0;
- }
- //ConsoleMethod( NetObject, getClientObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
- //{
- // NetObject *obj = object->getClientObject();
- // if ( obj )
- // return obj->getId();
- //
- // return NULL;
- //}
- DefineEngineMethod( NetObject, getServerObject, S32, (),,
- "@brief Returns a pointer to the client object when on a local connection.\n\n"
- "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation.\n\n"
-
- "@returns The SimObject ID of the server object.\n"
- "@tsexample\n"
- "// Psuedo-code, some values left out for this example\n"
- "%node = new ParticleEmitterNode(){};\n"
- "%serverObject = %node.getServerObject();\n"
- "if(isObject(%serverObject)\n"
- " %serverObject.setTransform(\"0 0 0\");\n\n"
- "@endtsexample\n\n"
-
- "@see @ref local_connections")
- {
- NetObject *obj = object->getServerObject();
- if ( obj )
- return obj->getId();
-
- return 0;
- }
- //ConsoleMethod( NetObject, getServerObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
- //{
- // NetObject *obj = object->getServerObject();
- // if ( obj )
- // return obj->getId();
- //
- // return NULL;
- //}
- DefineEngineMethod( NetObject, isClientObject, bool, (),,
- "@brief Called to check if an object resides on the clientside.\n\n"
- "@return True if the object resides on the client, false otherwise.")
- {
- return object->isClientObject();
- }
- //ConsoleMethod( NetObject, isClientObject, bool, 2, 2, "Return true for client-side objects." )
- //{
- // return object->isClientObject();
- //}
- DefineEngineMethod( NetObject, isServerObject, bool, (),,
- "@brief Checks if an object resides on the server.\n\n"
- "@return True if the object resides on the server, false otherwise.")
- {
- return object->isServerObject();
- }
- //ConsoleMethod( NetObject, isServerObject, bool, 2, 2, "Return true for client-side objects." )
- //{
- // return object->isServerObject();
- //}
- #ifdef TORQUE_AFX_ENABLED
- U16 NetObject::addScopeRef()
- {
- if (mScope_refs == 0)
- {
- mScope_id = arcaneFX::generateScopeId();
- onScopeIdChange();
- }
- mScope_refs++;
- return mScope_id;
- }
- void NetObject::removeScopeRef()
- {
- if (mScope_refs == 0)
- return;
- mScope_refs--;
- if (mScope_refs == 0)
- {
- mScope_id = 0;
- onScopeIdChange();
- }
- }
- #endif
- //Networked fields
- //------------------------------------------------------------------
- void NetObject::addNetworkedField(const char* in_pFieldname,
- const U32 in_fieldType,
- const dsize_t in_fieldOffset,
- const char* in_pFieldDocs,
- U32 flags,
- U32 networkMask)
- {
- addNetworkedField(
- in_pFieldname,
- in_fieldType,
- in_fieldOffset,
- 1,
- in_pFieldDocs,
- flags,
- networkMask);
- }
- void NetObject::addNetworkedField(const char* in_pFieldname,
- const U32 in_fieldType,
- const dsize_t in_fieldOffset,
- AbstractClassRep::WriteDataNotify in_writeDataFn,
- const char* in_pFieldDocs,
- U32 flags,
- U32 networkMask)
- {
- addNetworkedField(
- in_pFieldname,
- in_fieldType,
- in_fieldOffset,
- in_writeDataFn,
- 1,
- in_pFieldDocs,
- flags,
- networkMask);
- }
- void NetObject::addNetworkedField(const char* in_pFieldname,
- const U32 in_fieldType,
- const dsize_t in_fieldOffset,
- const U32 in_elementCount,
- const char* in_pFieldDocs,
- U32 flags,
- U32 networkMask)
- {
- addNetworkedField(in_pFieldname,
- in_fieldType,
- in_fieldOffset,
- &defaultProtectedWriteFn,
- in_elementCount,
- in_pFieldDocs,
- flags,
- networkMask);
- }
- void NetObject::addNetworkedField(const char* in_pFieldname,
- const U32 in_fieldType,
- const dsize_t in_fieldOffset,
- AbstractClassRep::WriteDataNotify in_writeDataFn,
- const U32 in_elementCount,
- const char* in_pFieldDocs,
- U32 flags,
- U32 networkMask)
- {
- AbstractClassRep::Field f;
- f.pFieldname = StringTable->insert(in_pFieldname);
- if (in_pFieldDocs)
- f.pFieldDocs = in_pFieldDocs;
- f.type = in_fieldType;
- f.offset = in_fieldOffset;
- f.elementCount = in_elementCount;
- f.validator = NULL;
- f.flag = flags;
- f.setDataFn = &defaultProtectedSetFn;
- f.getDataFn = &defaultProtectedGetFn;
- f.writeDataFn = in_writeDataFn;
- f.networkMask = networkMask;
- ConsoleBaseType* conType = ConsoleBaseType::getType(in_fieldType);
- AssertFatal(conType, "ConsoleObject::addField - invalid console type");
- f.table = conType->getEnumTable();
- sg_tempFieldList.push_back(f);
- }
- DefineEngineMethod(NetObject, clearScopeAlways, void, (), ,
- "@brief Clears the scope always flag on this object.\n\n")
- {
- object->clearScopeAlways();
- }
|