mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
837 lines
28 KiB
C++
837 lines
28 KiB
C++
|
|
//-----------------------------------------------------------------------------
|
||
|
|
// 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.
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
#include "platform/platform.h"
|
||
|
|
#include "console/consoleObject.h"
|
||
|
|
|
||
|
|
#include "core/stringTable.h"
|
||
|
|
#include "core/crc.h"
|
||
|
|
#include "core/dataChunker.h"
|
||
|
|
#include "console/console.h"
|
||
|
|
#include "console/consoleInternal.h"
|
||
|
|
#include "console/typeValidators.h"
|
||
|
|
#include "console/simObject.h"
|
||
|
|
#include "console/engineTypes.h"
|
||
|
|
#include "console/engineAPI.h"
|
||
|
|
|
||
|
|
|
||
|
|
IMPLEMENT_SCOPE( ConsoleAPI, Console,,
|
||
|
|
"Functionality related to the legacy TorqueScript console system." );
|
||
|
|
|
||
|
|
IMPLEMENT_NONINSTANTIABLE_CLASS( ConsoleObject,
|
||
|
|
"Legacy console system root class. Will disappear." )
|
||
|
|
END_IMPLEMENT_CLASS;
|
||
|
|
|
||
|
|
|
||
|
|
AbstractClassRep * AbstractClassRep::classLinkList = NULL;
|
||
|
|
AbstractClassRep::FieldList sg_tempFieldList( __FILE__, __LINE__ );
|
||
|
|
U32 AbstractClassRep::NetClassCount [NetClassGroupsCount][NetClassTypesCount] = {{0, },};
|
||
|
|
U32 AbstractClassRep::NetClassBitSize[NetClassGroupsCount][NetClassTypesCount] = {{0, },};
|
||
|
|
|
||
|
|
AbstractClassRep ** AbstractClassRep::classTable[NetClassGroupsCount][NetClassTypesCount];
|
||
|
|
|
||
|
|
U32 AbstractClassRep::classCRC[NetClassGroupsCount] = {CRC::INITIAL_CRC_VALUE, };
|
||
|
|
bool AbstractClassRep::initialized = false;
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
|
||
|
|
void AbstractClassRep::init()
|
||
|
|
{
|
||
|
|
// Only add the renderable and selectable globals for
|
||
|
|
// classes derived from SceneObject which are the only
|
||
|
|
// objects for which these work.
|
||
|
|
if ( isSubclassOf( "SceneObject" ) )
|
||
|
|
{
|
||
|
|
Con::addVariable( avar( "$%s::isRenderable", getClassName() ), TypeBool, &mIsRenderEnabled,
|
||
|
|
"@brief Disables rendering of all instances of this type.\n\n" );
|
||
|
|
|
||
|
|
Con::addVariable( avar( "$%s::isSelectable", getClassName() ), TypeBool, &mIsSelectionEnabled,
|
||
|
|
"@brief Disables selection of all instances of this type.\n\n" );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const AbstractClassRep::Field *AbstractClassRep::findField(StringTableEntry name) const
|
||
|
|
{
|
||
|
|
for(U32 i = 0; i < mFieldList.size(); i++)
|
||
|
|
if(mFieldList[i].pFieldname == name)
|
||
|
|
return &mFieldList[i];
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
AbstractClassRep* AbstractClassRep::findClassRep(const char* in_pClassName)
|
||
|
|
{
|
||
|
|
AssertFatal(initialized,
|
||
|
|
"AbstractClassRep::findClassRep() - Tried to find an AbstractClassRep before AbstractClassRep::initialize().");
|
||
|
|
|
||
|
|
for (AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
|
||
|
|
if (!dStricmp(walk->getClassName(), in_pClassName))
|
||
|
|
return walk;
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
AbstractClassRep* AbstractClassRep::findClassRep( U32 groupId, U32 typeId, U32 classId )
|
||
|
|
{
|
||
|
|
AssertFatal(initialized,
|
||
|
|
"AbstractClassRep::findClasRep() - Tried to create an object before AbstractClassRep::initialize().");
|
||
|
|
AssertFatal(classId < NetClassCount[groupId][typeId],
|
||
|
|
"AbstractClassRep::findClassRep() - Class id out of range.");
|
||
|
|
AssertFatal(classTable[groupId][typeId][classId] != NULL,
|
||
|
|
"AbstractClassRep::findClassRep() - No class with requested ID type.");
|
||
|
|
|
||
|
|
// Look up the specified class and create it.
|
||
|
|
if(classTable[groupId][typeId][classId])
|
||
|
|
return classTable[groupId][typeId][classId];
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
void AbstractClassRep::registerClassRep(AbstractClassRep* in_pRep)
|
||
|
|
{
|
||
|
|
AssertFatal(in_pRep != NULL, "AbstractClassRep::registerClassRep was passed a NULL pointer!");
|
||
|
|
|
||
|
|
#ifdef TORQUE_DEBUG // assert if this class is already registered.
|
||
|
|
for(AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
|
||
|
|
{
|
||
|
|
AssertFatal(dStrcmp(in_pRep->mClassName, walk->mClassName),
|
||
|
|
"Duplicate class name registered in AbstractClassRep::registerClassRep()");
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
in_pRep->nextClass = classLinkList;
|
||
|
|
classLinkList = in_pRep;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
void AbstractClassRep::removeClassRep(AbstractClassRep* in_pRep)
|
||
|
|
{
|
||
|
|
for( AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass )
|
||
|
|
{
|
||
|
|
// This is the case that will most likely get hit.
|
||
|
|
if( walk->nextClass == in_pRep )
|
||
|
|
walk->nextClass = walk->nextClass->nextClass;
|
||
|
|
else if( walk == in_pRep )
|
||
|
|
{
|
||
|
|
AssertFatal( in_pRep == classLinkList, "Pat failed in his logic for un linking RuntimeClassReps from the class linked list" );
|
||
|
|
classLinkList = in_pRep->nextClass;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
|
||
|
|
ConsoleObject* AbstractClassRep::create(const char* in_pClassName)
|
||
|
|
{
|
||
|
|
AssertFatal(initialized,
|
||
|
|
"AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize().");
|
||
|
|
|
||
|
|
const AbstractClassRep *rep = AbstractClassRep::findClassRep(in_pClassName);
|
||
|
|
if(rep)
|
||
|
|
return rep->create();
|
||
|
|
|
||
|
|
AssertWarn(0, avar("Couldn't find class rep for dynamic class: %s", in_pClassName));
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
ConsoleObject* AbstractClassRep::create(const U32 groupId, const U32 typeId, const U32 in_classId)
|
||
|
|
{
|
||
|
|
AbstractClassRep* classRep = findClassRep( groupId, typeId, in_classId );
|
||
|
|
if( !classRep )
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
return classRep->create();
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
|
||
|
|
static S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr)
|
||
|
|
{
|
||
|
|
const AbstractClassRep *a = *((const AbstractClassRep **) aptr);
|
||
|
|
const AbstractClassRep *b = *((const AbstractClassRep **) bptr);
|
||
|
|
|
||
|
|
if(a->mClassType != b->mClassType)
|
||
|
|
return a->mClassType - b->mClassType;
|
||
|
|
return dStrnatcasecmp(a->getClassName(), b->getClassName());
|
||
|
|
}
|
||
|
|
|
||
|
|
void AbstractClassRep::initialize()
|
||
|
|
{
|
||
|
|
AssertFatal(!initialized, "Duplicate call to AbstractClassRep::initialize()!");
|
||
|
|
Vector<AbstractClassRep *> dynamicTable(__FILE__, __LINE__);
|
||
|
|
|
||
|
|
AbstractClassRep *walk;
|
||
|
|
|
||
|
|
// Initialize namespace references...
|
||
|
|
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||
|
|
{
|
||
|
|
walk->mNamespace = Con::lookupNamespace(StringTable->insert(walk->getClassName()));
|
||
|
|
walk->mNamespace->mUsage = walk->getDocString();
|
||
|
|
walk->mNamespace->mClassRep = walk;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize field lists... (and perform other console registration).
|
||
|
|
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||
|
|
{
|
||
|
|
// sg_tempFieldList is used as a staging area for field lists
|
||
|
|
// (see addField, addGroup, etc.)
|
||
|
|
sg_tempFieldList.setSize(0);
|
||
|
|
|
||
|
|
walk->init();
|
||
|
|
|
||
|
|
// So if we have things in it, copy it over...
|
||
|
|
if (sg_tempFieldList.size() != 0)
|
||
|
|
walk->mFieldList = sg_tempFieldList;
|
||
|
|
|
||
|
|
// And of course delete it every round.
|
||
|
|
sg_tempFieldList.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate counts and bit sizes for the various NetClasses.
|
||
|
|
for (U32 group = 0; group < NetClassGroupsCount; group++)
|
||
|
|
{
|
||
|
|
U32 groupMask = 1 << group;
|
||
|
|
|
||
|
|
// Specifically, for each NetClass of each NetGroup...
|
||
|
|
for(U32 type = 0; type < NetClassTypesCount; type++)
|
||
|
|
{
|
||
|
|
// Go through all the classes and find matches...
|
||
|
|
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||
|
|
{
|
||
|
|
if(walk->mClassType == type && walk->mClassGroupMask & groupMask)
|
||
|
|
dynamicTable.push_back(walk);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set the count for this NetGroup and NetClass
|
||
|
|
NetClassCount[group][type] = dynamicTable.size();
|
||
|
|
if(!NetClassCount[group][type])
|
||
|
|
continue; // If no classes matched, skip to next.
|
||
|
|
|
||
|
|
// Sort by type and then by name.
|
||
|
|
dQsort((void *) &dynamicTable[0], dynamicTable.size(), sizeof(AbstractClassRep *), ACRCompare);
|
||
|
|
|
||
|
|
// Allocate storage in the classTable
|
||
|
|
classTable[group][type] = new AbstractClassRep*[NetClassCount[group][type]];
|
||
|
|
|
||
|
|
// Fill this in and assign class ids for this group.
|
||
|
|
for(U32 i = 0; i < NetClassCount[group][type];i++)
|
||
|
|
{
|
||
|
|
classTable[group][type][i] = dynamicTable[i];
|
||
|
|
dynamicTable[i]->mClassId[group] = i;
|
||
|
|
}
|
||
|
|
|
||
|
|
// And calculate the size of bitfields for this group and type.
|
||
|
|
NetClassBitSize[group][type] =
|
||
|
|
getBinLog2(getNextPow2(NetClassCount[group][type] + 1));
|
||
|
|
AssertFatal(NetClassCount[group][type] < (1 << NetClassBitSize[group][type]), "NetClassBitSize too small!");
|
||
|
|
|
||
|
|
dynamicTable.clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ok, we're golden!
|
||
|
|
initialized = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void AbstractClassRep::shutdown()
|
||
|
|
{
|
||
|
|
AssertFatal( initialized, "AbstractClassRep::shutdown - not initialized" );
|
||
|
|
|
||
|
|
// Release storage allocated to the class table.
|
||
|
|
|
||
|
|
for (U32 group = 0; group < NetClassGroupsCount; group++)
|
||
|
|
for(U32 type = 0; type < NetClassTypesCount; type++)
|
||
|
|
if( classTable[ group ][ type ] )
|
||
|
|
SAFE_DELETE_ARRAY( classTable[ group ][ type ] );
|
||
|
|
|
||
|
|
initialized = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
AbstractClassRep *AbstractClassRep::getCommonParent( const AbstractClassRep *otherClass ) const
|
||
|
|
{
|
||
|
|
// CodeReview: This may be a noob way of doing it. There may be some kind of
|
||
|
|
// super-spiffy algorithm to do what the code below does, but this appeared
|
||
|
|
// to make sense to me, and it is pretty easy to see what it is doing [6/23/2007 Pat]
|
||
|
|
|
||
|
|
static VectorPtr<AbstractClassRep *> thisClassHeirarchy;
|
||
|
|
thisClassHeirarchy.clear();
|
||
|
|
|
||
|
|
AbstractClassRep *walk = const_cast<AbstractClassRep *>( this );
|
||
|
|
|
||
|
|
while( walk != NULL )
|
||
|
|
{
|
||
|
|
thisClassHeirarchy.push_front( walk );
|
||
|
|
walk = walk->getParentClass();
|
||
|
|
}
|
||
|
|
|
||
|
|
static VectorPtr<AbstractClassRep *> compClassHeirarchy;
|
||
|
|
compClassHeirarchy.clear();
|
||
|
|
walk = const_cast<AbstractClassRep *>( otherClass );
|
||
|
|
while( walk != NULL )
|
||
|
|
{
|
||
|
|
compClassHeirarchy.push_front( walk );
|
||
|
|
walk = walk->getParentClass();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Make sure we only iterate over the list the number of times we can
|
||
|
|
S32 maxIterations = getMin( compClassHeirarchy.size(), thisClassHeirarchy.size() );
|
||
|
|
|
||
|
|
U32 i = 0;
|
||
|
|
for( ; i < maxIterations; i++ )
|
||
|
|
{
|
||
|
|
if( compClassHeirarchy[i] != thisClassHeirarchy[i] )
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return compClassHeirarchy[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
//-------------------------------------- ConsoleObject
|
||
|
|
|
||
|
|
static char replacebuf[1024];
|
||
|
|
static char* suppressSpaces(const char* in_pname)
|
||
|
|
{
|
||
|
|
U32 i = 0;
|
||
|
|
char chr;
|
||
|
|
do
|
||
|
|
{
|
||
|
|
chr = in_pname[i];
|
||
|
|
replacebuf[i++] = (chr != 32) ? chr : '_';
|
||
|
|
} while(chr);
|
||
|
|
|
||
|
|
return replacebuf;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addGroup(const char* in_pGroupname, const char* in_pGroupDocs)
|
||
|
|
{
|
||
|
|
// Remove spaces.
|
||
|
|
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
|
||
|
|
|
||
|
|
// Append group type to fieldname.
|
||
|
|
dStrcat(pFieldNameBuf, "_begingroup");
|
||
|
|
|
||
|
|
// Create Field.
|
||
|
|
AbstractClassRep::Field f;
|
||
|
|
f.pFieldname = StringTable->insert(pFieldNameBuf);
|
||
|
|
f.pGroupname = in_pGroupname;
|
||
|
|
|
||
|
|
if(in_pGroupDocs)
|
||
|
|
f.pFieldDocs = in_pGroupDocs;
|
||
|
|
|
||
|
|
f.type = AbstractClassRep::StartGroupFieldType;
|
||
|
|
f.elementCount = 0;
|
||
|
|
f.groupExpand = false;
|
||
|
|
f.validator = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
|
||
|
|
// Add to field list.
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::endGroup(const char* in_pGroupname)
|
||
|
|
{
|
||
|
|
// Remove spaces.
|
||
|
|
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
|
||
|
|
|
||
|
|
// Append group type to fieldname.
|
||
|
|
dStrcat(pFieldNameBuf, "_endgroup");
|
||
|
|
|
||
|
|
// Create Field.
|
||
|
|
AbstractClassRep::Field f;
|
||
|
|
f.pFieldname = StringTable->insert(pFieldNameBuf);
|
||
|
|
f.pGroupname = in_pGroupname;
|
||
|
|
f.type = AbstractClassRep::EndGroupFieldType;
|
||
|
|
f.groupExpand = false;
|
||
|
|
f.validator = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
f.elementCount = 0;
|
||
|
|
|
||
|
|
// Add to field list.
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addArray( const char *arrayName, S32 count )
|
||
|
|
{
|
||
|
|
char *nameBuff = suppressSpaces(arrayName);
|
||
|
|
dStrcat(nameBuff, "_beginarray");
|
||
|
|
|
||
|
|
// Create Field.
|
||
|
|
AbstractClassRep::Field f;
|
||
|
|
f.pFieldname = StringTable->insert(nameBuff);
|
||
|
|
f.pGroupname = arrayName;
|
||
|
|
|
||
|
|
f.type = AbstractClassRep::StartArrayFieldType;
|
||
|
|
f.elementCount = count;
|
||
|
|
f.groupExpand = false;
|
||
|
|
f.validator = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
|
||
|
|
// Add to field list.
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::endArray( const char *arrayName )
|
||
|
|
{
|
||
|
|
char *nameBuff = suppressSpaces(arrayName);
|
||
|
|
dStrcat(nameBuff, "_endarray");
|
||
|
|
|
||
|
|
// Create Field.
|
||
|
|
AbstractClassRep::Field f;
|
||
|
|
f.pFieldname = StringTable->insert(nameBuff);
|
||
|
|
f.pGroupname = arrayName;
|
||
|
|
f.type = AbstractClassRep::EndArrayFieldType;
|
||
|
|
f.groupExpand = false;
|
||
|
|
f.validator = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
f.elementCount = 0;
|
||
|
|
|
||
|
|
// Add to field list.
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addField(const char* in_pFieldname,
|
||
|
|
const U32 in_fieldType,
|
||
|
|
const dsize_t in_fieldOffset,
|
||
|
|
const char* in_pFieldDocs,
|
||
|
|
U32 flags )
|
||
|
|
{
|
||
|
|
addField(
|
||
|
|
in_pFieldname,
|
||
|
|
in_fieldType,
|
||
|
|
in_fieldOffset,
|
||
|
|
1,
|
||
|
|
in_pFieldDocs,
|
||
|
|
flags );
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addProtectedField(const char* in_pFieldname,
|
||
|
|
const U32 in_fieldType,
|
||
|
|
const dsize_t in_fieldOffset,
|
||
|
|
AbstractClassRep::SetDataNotify in_setDataFn,
|
||
|
|
AbstractClassRep::GetDataNotify in_getDataFn,
|
||
|
|
const char* in_pFieldDocs,
|
||
|
|
U32 flags )
|
||
|
|
{
|
||
|
|
addProtectedField(
|
||
|
|
in_pFieldname,
|
||
|
|
in_fieldType,
|
||
|
|
in_fieldOffset,
|
||
|
|
in_setDataFn,
|
||
|
|
in_getDataFn,
|
||
|
|
1,
|
||
|
|
in_pFieldDocs,
|
||
|
|
flags );
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void ConsoleObject::addField(const char* in_pFieldname,
|
||
|
|
const U32 in_fieldType,
|
||
|
|
const dsize_t in_fieldOffset,
|
||
|
|
const U32 in_elementCount,
|
||
|
|
const char* in_pFieldDocs,
|
||
|
|
U32 flags )
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
|
||
|
|
ConsoleBaseType* conType = ConsoleBaseType::getType( in_fieldType );
|
||
|
|
AssertFatal( conType, "ConsoleObject::addField - invalid console type" );
|
||
|
|
f.table = conType->getEnumTable();
|
||
|
|
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addProtectedField(const char* in_pFieldname,
|
||
|
|
const U32 in_fieldType,
|
||
|
|
const dsize_t in_fieldOffset,
|
||
|
|
AbstractClassRep::SetDataNotify in_setDataFn,
|
||
|
|
AbstractClassRep::GetDataNotify in_getDataFn,
|
||
|
|
const U32 in_elementCount,
|
||
|
|
const char* in_pFieldDocs,
|
||
|
|
U32 flags )
|
||
|
|
{
|
||
|
|
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 = in_setDataFn;
|
||
|
|
f.getDataFn = in_getDataFn;
|
||
|
|
|
||
|
|
ConsoleBaseType* conType = ConsoleBaseType::getType( in_fieldType );
|
||
|
|
AssertFatal( conType, "ConsoleObject::addProtectedField - invalid console type" );
|
||
|
|
f.table = conType->getEnumTable();
|
||
|
|
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addFieldV(const char* in_pFieldname,
|
||
|
|
const U32 in_fieldType,
|
||
|
|
const dsize_t in_fieldOffset,
|
||
|
|
TypeValidator *v,
|
||
|
|
const char* in_pFieldDocs)
|
||
|
|
{
|
||
|
|
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 = 1;
|
||
|
|
f.table = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
f.validator = v;
|
||
|
|
v->fieldIndex = sg_tempFieldList.size();
|
||
|
|
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::addDeprecatedField(const char *fieldName)
|
||
|
|
{
|
||
|
|
AbstractClassRep::Field f;
|
||
|
|
f.pFieldname = StringTable->insert(fieldName);
|
||
|
|
f.type = AbstractClassRep::DeprecatedFieldType;
|
||
|
|
f.offset = 0;
|
||
|
|
f.elementCount = 0;
|
||
|
|
f.table = NULL;
|
||
|
|
f.validator = NULL;
|
||
|
|
f.setDataFn = &defaultProtectedSetFn;
|
||
|
|
f.getDataFn = &defaultProtectedGetFn;
|
||
|
|
|
||
|
|
sg_tempFieldList.push_back(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool ConsoleObject::removeField(const char* in_pFieldname)
|
||
|
|
{
|
||
|
|
for (U32 i = 0; i < sg_tempFieldList.size(); i++) {
|
||
|
|
if (dStricmp(in_pFieldname, sg_tempFieldList[i].pFieldname) == 0) {
|
||
|
|
sg_tempFieldList.erase(i);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
void ConsoleObject::initPersistFields()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
void ConsoleObject::consoleInit()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
//--------------------------------------
|
||
|
|
AbstractClassRep* ConsoleObject::getClassRep() const
|
||
|
|
{
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
String ConsoleObject::_getLogMessage(const char* fmt, void* args) const
|
||
|
|
{
|
||
|
|
String objClass = "UnknownClass";
|
||
|
|
if(getClassRep())
|
||
|
|
objClass = getClassRep()->getClassName();
|
||
|
|
|
||
|
|
String formattedMessage = String::VToString(fmt, args);
|
||
|
|
return String::ToString("%s - Object at %x - %s",
|
||
|
|
objClass.c_str(), this, formattedMessage.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::logMessage(const char* fmt, ...) const
|
||
|
|
{
|
||
|
|
va_list args;
|
||
|
|
va_start(args, fmt);
|
||
|
|
Con::printf(_getLogMessage(fmt, args));
|
||
|
|
va_end(args);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::logWarning(const char* fmt, ...) const
|
||
|
|
{
|
||
|
|
va_list args;
|
||
|
|
va_start(args, fmt);
|
||
|
|
Con::warnf(_getLogMessage(fmt, args));
|
||
|
|
va_end(args);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ConsoleObject::logError(const char* fmt, ...) const
|
||
|
|
{
|
||
|
|
va_list args;
|
||
|
|
va_start(args, fmt);
|
||
|
|
Con::errorf(_getLogMessage(fmt, args));
|
||
|
|
va_end(args);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
static const char* returnClassList( Vector< AbstractClassRep* >& classes, U32 bufSize )
|
||
|
|
{
|
||
|
|
if( !classes.size() )
|
||
|
|
return "";
|
||
|
|
|
||
|
|
dQsort( classes.address(), classes.size(), sizeof( AbstractClassRep* ), ACRCompare );
|
||
|
|
|
||
|
|
char* ret = Con::getReturnBuffer( bufSize );
|
||
|
|
dStrcpy( ret, classes[ 0 ]->getClassName() );
|
||
|
|
for( U32 i = 1; i < classes.size(); i ++ )
|
||
|
|
{
|
||
|
|
dStrcat( ret, "\t" );
|
||
|
|
dStrcat( ret, classes[ i ]->getClassName() );
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
DefineEngineFunction( isClass, bool, ( const char* identifier ),,
|
||
|
|
"@brief Returns true if the passed identifier is the name of a declared class.\n\n"
|
||
|
|
"@ingroup Console")
|
||
|
|
{
|
||
|
|
AbstractClassRep* rep = AbstractClassRep::findClassRep( identifier );
|
||
|
|
return rep != NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( isMemberOfClass, bool, ( const char* className, const char* superClassName ),,
|
||
|
|
"@brief Returns true if the class is derived from the super class.\n\n"
|
||
|
|
"If either class doesn't exist this returns false.\n"
|
||
|
|
"@param className The class name.\n"
|
||
|
|
"@param superClassName The super class to look for.\n"
|
||
|
|
"@ingroup Console")
|
||
|
|
{
|
||
|
|
AbstractClassRep *pRep = AbstractClassRep::findClassRep( className );
|
||
|
|
while (pRep)
|
||
|
|
{
|
||
|
|
if( !dStricmp( pRep->getClassName(), superClassName ) )
|
||
|
|
return true;
|
||
|
|
pRep = pRep->getParentClass();
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( getDescriptionOfClass, const char*, ( const char* className ),,
|
||
|
|
"@brief Returns the description string for the named class.\n\n"
|
||
|
|
"@param className The name of the class.\n"
|
||
|
|
"@return The class description in string format.\n"
|
||
|
|
"@ingroup Console")
|
||
|
|
{
|
||
|
|
AbstractClassRep* rep = AbstractClassRep::findClassRep( className );
|
||
|
|
if( rep )
|
||
|
|
return rep->getDescription();
|
||
|
|
|
||
|
|
Con::errorf( "getDescriptionOfClass - no class called '%s'", className );
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( getCategoryOfClass, const char*, ( const char* className ),,
|
||
|
|
"@brief Returns the category of the given class.\n\n"
|
||
|
|
"@param className The name of the class.\n"
|
||
|
|
"@ingroup Console")
|
||
|
|
{
|
||
|
|
AbstractClassRep* rep = AbstractClassRep::findClassRep( className );
|
||
|
|
if( rep )
|
||
|
|
return rep->getCategory();
|
||
|
|
|
||
|
|
Con::errorf( "getCategoryOfClass - no class called '%s'", className );
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( enumerateConsoleClasses, const char*, ( const char* className ), ( "" ),
|
||
|
|
"@brief Returns a list of classes that derive from the named class.\n\n"
|
||
|
|
"If the named class is omitted this dumps all the classes.\n"
|
||
|
|
"@param className The optional base class name.\n"
|
||
|
|
"@return A tab delimited list of classes.\n"
|
||
|
|
"@ingroup Editors\n"
|
||
|
|
"@internal")
|
||
|
|
{
|
||
|
|
AbstractClassRep *base = NULL;
|
||
|
|
if(className && *className)
|
||
|
|
{
|
||
|
|
base = AbstractClassRep::findClassRep(className);
|
||
|
|
if(!base)
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
Vector<AbstractClassRep*> classes;
|
||
|
|
U32 bufSize = 0;
|
||
|
|
for(AbstractClassRep *rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass())
|
||
|
|
{
|
||
|
|
if( !base || rep->isClass(base))
|
||
|
|
{
|
||
|
|
classes.push_back(rep);
|
||
|
|
bufSize += dStrlen(rep->getClassName()) + 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return returnClassList( classes, bufSize );
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( enumerateConsoleClassesByCategory, const char*, ( String category ),,
|
||
|
|
"@brief Provide a list of classes that belong to the given category.\n\n"
|
||
|
|
"@param category The category name.\n"
|
||
|
|
"@return A tab delimited list of classes.\n"
|
||
|
|
"@ingroup Editors\n"
|
||
|
|
"@internal")
|
||
|
|
{
|
||
|
|
U32 categoryLength = category.length();
|
||
|
|
|
||
|
|
U32 bufSize = 0;
|
||
|
|
Vector< AbstractClassRep* > classes;
|
||
|
|
|
||
|
|
for( AbstractClassRep* rep = AbstractClassRep::getClassList(); rep != NULL; rep = rep->getNextClass() )
|
||
|
|
{
|
||
|
|
const String& repCategory = rep->getCategory();
|
||
|
|
|
||
|
|
if( repCategory.length() >= categoryLength
|
||
|
|
&& ( repCategory.compare( category, categoryLength, String::NoCase ) == 0 )
|
||
|
|
&& ( repCategory[ categoryLength ] == ' ' || repCategory[ categoryLength ] == '\0' ) )
|
||
|
|
{
|
||
|
|
classes.push_back( rep );
|
||
|
|
bufSize += dStrlen( rep->getClassName() + 1 );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return returnClassList( classes, bufSize );
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( dumpNetStats, void, (),,
|
||
|
|
"@brief Dumps network statistics for each class to the console.\n\n"
|
||
|
|
|
||
|
|
"The returned <i>avg</i>, <i>min</i> and <i>max</i> values are in bits sent per update. "
|
||
|
|
"The <i>num</i> value is the total number of events collected.\n"
|
||
|
|
|
||
|
|
"@note This method only works when TORQUE_NET_STATS is defined in torqueConfig.h.\n"
|
||
|
|
"@ingroup Networking\n" )
|
||
|
|
{
|
||
|
|
#ifdef TORQUE_NET_STATS
|
||
|
|
for (AbstractClassRep * rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass())
|
||
|
|
{
|
||
|
|
if (rep->mNetStatPack.numEvents || rep->mNetStatUnpack.numEvents || rep->mNetStatWrite.numEvents || rep->mNetStatRead.numEvents)
|
||
|
|
{
|
||
|
|
Con::printf("class %s net info",rep->getClassName());
|
||
|
|
if (rep->mNetStatPack.numEvents)
|
||
|
|
Con::printf(" packUpdate: avg (%f), min (%i), max (%i), num (%i)",
|
||
|
|
F32(rep->mNetStatPack.total)/F32(rep->mNetStatPack.numEvents),
|
||
|
|
rep->mNetStatPack.min,
|
||
|
|
rep->mNetStatPack.max,
|
||
|
|
rep->mNetStatPack.numEvents);
|
||
|
|
if (rep->mNetStatUnpack.numEvents)
|
||
|
|
Con::printf(" unpackUpdate: avg (%f), min (%i), max (%i), num (%i)",
|
||
|
|
F32(rep->mNetStatUnpack.total)/F32(rep->mNetStatUnpack.numEvents),
|
||
|
|
rep->mNetStatUnpack.min,
|
||
|
|
rep->mNetStatUnpack.max,
|
||
|
|
rep->mNetStatUnpack.numEvents);
|
||
|
|
if (rep->mNetStatWrite.numEvents)
|
||
|
|
Con::printf(" write: avg (%f), min (%i), max (%i), num (%i)",
|
||
|
|
F32(rep->mNetStatWrite.total)/F32(rep->mNetStatWrite.numEvents),
|
||
|
|
rep->mNetStatWrite.min,
|
||
|
|
rep->mNetStatWrite.max,
|
||
|
|
rep->mNetStatWrite.numEvents);
|
||
|
|
if (rep->mNetStatRead.numEvents)
|
||
|
|
Con::printf(" read: avg (%f), min (%i), max (%i), num (%i)",
|
||
|
|
F32(rep->mNetStatRead.total)/F32(rep->mNetStatRead.numEvents),
|
||
|
|
rep->mNetStatRead.min,
|
||
|
|
rep->mNetStatRead.max,
|
||
|
|
rep->mNetStatRead.numEvents);
|
||
|
|
S32 sum = 0;
|
||
|
|
for (S32 i=0; i<32; i++)
|
||
|
|
sum += rep->mDirtyMaskFrequency[i];
|
||
|
|
if (sum)
|
||
|
|
{
|
||
|
|
Con::printf(" Mask bits:");
|
||
|
|
for (S32 i=0; i<8; i++)
|
||
|
|
{
|
||
|
|
F32 avg0 = rep->mDirtyMaskFrequency[i] ? F32(rep->mDirtyMaskTotal[i])/F32(rep->mDirtyMaskFrequency[i]) : 0.0f;
|
||
|
|
F32 avg8 = rep->mDirtyMaskFrequency[i+8] ? F32(rep->mDirtyMaskTotal[i+8])/F32(rep->mDirtyMaskFrequency[i+8]) : 0.0f;
|
||
|
|
F32 avg16 = rep->mDirtyMaskFrequency[i+16] ? F32(rep->mDirtyMaskTotal[i+16])/F32(rep->mDirtyMaskFrequency[i+16]) : 0.0f;
|
||
|
|
F32 avg24 = rep->mDirtyMaskFrequency[i+24] ? F32(rep->mDirtyMaskTotal[i+24])/F32(rep->mDirtyMaskFrequency[i+24]) : 0.0f;
|
||
|
|
Con::printf(" %2i - %4i (%6.2f) %2i - %4i (%6.2f) %2i - %4i (%6.2f) %2i - %4i, (%6.2f)",
|
||
|
|
i ,rep->mDirtyMaskFrequency[i],avg0,
|
||
|
|
i+8 ,rep->mDirtyMaskFrequency[i+8],avg8,
|
||
|
|
i+16,rep->mDirtyMaskFrequency[i+16],avg16,
|
||
|
|
i+24,rep->mDirtyMaskFrequency[i+24],avg24);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
rep->resetNetStats();
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
DefineEngineFunction( sizeof, S32, ( const char *objectOrClass ),,
|
||
|
|
"@brief Determines the memory consumption of a class or object.\n\n"
|
||
|
|
"@param objectOrClass The object or class being measured.\n"
|
||
|
|
"@return Returns the total size of an object in bytes.\n"
|
||
|
|
"@ingroup Debugging\n")
|
||
|
|
{
|
||
|
|
AbstractClassRep *acr = NULL;
|
||
|
|
SimObject *obj = Sim::findObject(objectOrClass);
|
||
|
|
if(obj)
|
||
|
|
acr = obj->getClassRep();
|
||
|
|
|
||
|
|
if(!acr)
|
||
|
|
acr = AbstractClassRep::findClassRep(objectOrClass);
|
||
|
|
|
||
|
|
if(acr)
|
||
|
|
return acr->getSizeof();
|
||
|
|
|
||
|
|
if(dStricmp("ConsoleObject", objectOrClass) == 0)
|
||
|
|
return sizeof(ConsoleObject);
|
||
|
|
|
||
|
|
Con::warnf("could not find a class rep for that object or class name.");
|
||
|
|
return 0;
|
||
|
|
}
|