diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 049742e50..c3a45c3be 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -40,6 +40,7 @@ #include "core/fileObject.h" #include "persistence/taml/tamlCustom.h" #include "gui/editor/guiInspector.h" +#include "console/script.h" #include "sim/netObject.h" @@ -90,7 +91,7 @@ SimObject::SimObject() mNameSpace = NULL; mNotifyList = NULL; mFlags.set( ModStaticFields | ModDynamicFields ); - + mPrototype = true; mProgenitorFile = StringTable->EmptyString(); mFieldDictionary = NULL; @@ -159,7 +160,8 @@ void SimObject::initPersistFields() addProtectedField("inheritFrom", TypeString, Offset(mInheritFrom, SimObject), &setInheritFrom, &defaultProtectedGetFn, "Optional Name of object to inherit from as a parent."); - + + addProtectedField("Prototype", TypeBool, Offset(mPrototype, SimObject), &_doPrototype, &defaultProtectedGetFn, "Prototype Methods", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors); endGroup( "Ungrouped" ); addGroup( "Object" ); @@ -212,6 +214,15 @@ void SimObject::initPersistFields() } //----------------------------------------------------------------------------- +bool SimObject::_doPrototype(void* object, const char* index, const char* data) +{ + if (!Con::isFunction("PrototypeClass")) return false; + if (dAtoi(data) != 1) return false; + SimObject* obj = reinterpret_cast(object); + String command = String("PrototypeClass(") + (obj->getName()? String(obj->getName()) : String::ToString(obj->getId())) + ");"; + Con::evaluate(command.c_str()); + return false; +} String SimObject::describeSelf() const { @@ -2707,6 +2718,59 @@ DefineEngineMethod(SimObject, getMethodSigs, ArrayObject*, (bool commands), (fal return dictionary; } + +DefineEngineFunction(getMethodSigsNS, ArrayObject*, (StringTableEntry className, bool commands), (false), + "List the methods defined on this object.\n\n" + "Each description is a newline-separated vector with the following elements:\n" + "- method prototype string.\n" + "- Documentation string (not including prototype). This takes up the remainder of the vector.\n" + "@return An ArrayObject populated with (name,description) pairs of all methods defined on the object.") +{ + + Namespace* ns = Con::lookupNamespace(className); + if (!ns) + return 0; + + ArrayObject* dictionary = new ArrayObject(); + dictionary->registerObject(); + + VectorPtr vec(__FILE__, __LINE__); + ns->getEntryList(&vec); + for (Vector< Namespace::Entry* >::iterator j = vec.begin(); j != vec.end(); j++) + { + Namespace::Entry* e = *j; + + if (commands) + { + if ((e->mType < Namespace::Entry::ConsoleFunctionType)) + continue; + } + else + { + if ((e->mType > Namespace::Entry::ScriptCallbackType)) + continue; + } + StringBuilder str; + str.append("function "); + str.append(ns->getName()); + str.append("::"); + str.append(e->getPrototypeSig()); + str.append('\n'); + str.append("{"); + String docs = e->getDocString(); + if (!docs.isEmpty()) + { + str.append("\n/*"); + str.append(docs); + str.append("\n*/"); + } + str.append('\n'); + str.append("}"); + dictionary->push_back(e->mFunctionName, str.end()); + } + + return dictionary; +} //----------------------------------------------------------------------------- namespace { @@ -3262,6 +3326,33 @@ DefineEngineMethod( SimObject, getFieldCount, S32, (),, return list.size() - numDummyEntries; } +DefineEngineFunction(getFieldCountNS, S32, (StringTableEntry className), , + "Get the number of static fields on the name space.\n" + "@return The number of static fields defined on the object.") +{ + Namespace* ns = Con::lookupNamespace(className); + if (!ns) + return 0; + AbstractClassRep* rep = ns->mClassRep; + if (!rep) + return 0; + + const AbstractClassRep::FieldList& list = rep->mFieldList; + const AbstractClassRep::Field* f; + U32 numDummyEntries = 0; + + for (S32 i = 0; i < list.size(); i++) + { + f = &list[i]; + + // The special field types do not need to be counted. + if (f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors)) + numDummyEntries++; + } + + return list.size() - numDummyEntries; +} + //----------------------------------------------------------------------------- DefineEngineMethod( SimObject, getField, const char*, ( S32 index ),, @@ -3293,6 +3384,42 @@ DefineEngineMethod( SimObject, getField, const char*, ( S32 index ),, return ""; } +DefineEngineFunction(getFieldNS, const char*, (StringTableEntry className,S32 index), , + "Retrieve the value of a static field by index.\n" + "@param index The index of the static field.\n" + "@return The value of the static field with the given index or \"\".") +{ + Namespace* ns = Con::lookupNamespace(className); + if (!ns) + return 0; + AbstractClassRep* rep = ns->mClassRep; + if (!rep) + return 0; + + const AbstractClassRep::FieldList& list = rep->mFieldList; + if ((index < 0) || (index >= list.size())) + return ""; + + const AbstractClassRep::Field* f; + S32 currentField = 0; + for (U32 i = 0; i < list.size() && currentField <= index; i++) + { + f = &list[i]; + + // The special field types can be skipped. + if (f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors)) + continue; + + if (currentField == index) + return f->pFieldname; + + currentField++; + } + + // if we found nada, return nada. + return ""; +} + DefineEngineFunction(getClassHierarchy, const char*, (const char* name), , "Returns the inheritance hierarchy for a given class.") { diff --git a/Engine/source/console/simObject.h b/Engine/source/console/simObject.h index 912f1b086..947302f0e 100644 --- a/Engine/source/console/simObject.h +++ b/Engine/source/console/simObject.h @@ -300,7 +300,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks SimObject* nextIdObject; StringTableEntry mInheritFrom; - + bool mPrototype; /// SimGroup we're contained in, if any. SimGroup* mGroup; @@ -388,7 +388,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks public: inline void setProgenitorFile(const char* pFile) { mProgenitorFile = StringTable->insert(pFile); } inline StringTableEntry getProgenitorFile(void) const { return mProgenitorFile; } - + static bool _doPrototype(void* object, const char* index, const char* data); protected: /// Taml callbacks. virtual void onTamlPreWrite(void) {} diff --git a/Templates/BaseGame/game/data/Prototyping/Prototyping.tscript b/Templates/BaseGame/game/data/Prototyping/Prototyping.tscript index c053fd2cf..abdf546be 100644 --- a/Templates/BaseGame/game/data/Prototyping/Prototyping.tscript +++ b/Templates/BaseGame/game/data/Prototyping/Prototyping.tscript @@ -37,6 +37,9 @@ function Prototyping::onDestroyGameServer(%this) //This is called when the client is initially set up by the game application function Prototyping::initClient(%this) { + //class method prototyping + %this.queueExec("./UI/classPrototyping"); + %this.queueExec("./UI/classPrototyping.gui"); } //This is called when a client connects to a server diff --git a/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.asset.taml b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.asset.taml new file mode 100644 index 000000000..a269f2329 --- /dev/null +++ b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.asset.taml @@ -0,0 +1,5 @@ + diff --git a/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.gui b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.gui new file mode 100644 index 000000000..e01b92046 --- /dev/null +++ b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.gui @@ -0,0 +1,115 @@ +//--- OBJECT WRITE BEGIN --- +$guiContent = new GuiControl(classPrototyping) { + extent = "1024 768"; + profile = "GuiDefaultProfile"; + tooltipProfile = "GuiToolTipProfile"; + isContainer = "1"; + canSaveDynamicFields = "1"; + originalAssetName = "classPrototyping"; + + new GuiWindowCtrl() { + text = "Class Prototyping"; + position = "216 124"; + extent = "592 519"; + horizSizing = "center"; + vertSizing = "center"; + profile = "ToolsGuiWindowProfile"; + tooltipProfile = "GuiToolTipProfile"; + closeCommand = "Canvas.popDialog(classPrototyping);"; + new GuiScrollCtrl() { + lockVertScroll = "1"; + position = "14 30"; + extent = "564 33"; + profile = "ToolsGuiScrollProfile"; + tooltipProfile = "GuiToolTipProfile"; + + new GuiDynamicCtrlArrayControl(ClassInheritanceListCtrl) { + colCount = "1"; + colSize = "80"; + rowCount = "1"; + rowSize = "18"; + autoCellSize = "1"; + fillRowFirst = "0"; + dynamicSize = "1"; + position = "1 1"; + extent = "400 18"; + profile = "GuiDefaultProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + }; + new GuiTextCtrl() { + text = "Callbacks"; + position = "24 66"; + extent = "54 14"; + profile = "ToolsGuiTextProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + new GuiScrollCtrl() { + position = "19 80"; + extent = "552 326"; + profile = "ToolsGuiScrollProfile"; + tooltipProfile = "GuiToolTipProfile"; + + new GuiDynamicCtrlArrayControl(ClassMethodListCtrl) { + colCount = "1"; + colSize = "8000"; + rowCount = "1"; + rowSize = "18"; + dynamicSize = "1"; + extent = "552 326"; + profile = "GuiDefaultProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + }; + new GuiBitmapButtonCtrl() { + BitmapAsset = "ToolsModule:iconOpen_image"; + bitmapMode = "Centered"; + position = "348 467"; + extent = "22 22"; + horizSizing = "left"; + profile = "ToolsGuiButtonProfile"; + command = "SelectAssetPath.showDialog(AssetBrowser.dirHandler.currentAddress, \"setProtoTypeFilePath\");\nSelectAssetPathWindow.selectWindow();"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "New Module"; + }; + new GuiTextEditCtrl() { + text = "data/ExampleModule"; + position = "143 470"; + extent = "201 20"; + horizSizing = "width"; + profile = "ToolsGuiTextEditProfile"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + internalName = "targetPath"; + }; + new GuiTextCtrl() { + text = "Target Path:"; + position = "20 470"; + extent = "116 17"; + profile = "ToolsGuiTextProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + new GuiButtonCtrl() { + text = "Save"; + position = "431 465"; + profile = "ToolsGuiButtonProfile"; + command = "classPrototyping.writeResults();"; + tooltipProfile = "GuiToolTipProfile"; + }; + new GuiCheckBoxCtrl(ReportCommands) { + text = "Report Commands"; + position = "16 420"; + extent = "125 30"; + profile = "ToolsGuiCheckBoxProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + new GuiCheckBoxCtrl(ReportVariables) { + text = "Report Stock Variables"; + position = "152 420"; + extent = "125 30"; + profile = "ToolsGuiCheckBoxProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.tscript b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.tscript new file mode 100644 index 000000000..1a2ca8fac --- /dev/null +++ b/Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.tscript @@ -0,0 +1,204 @@ +function classPrototyping::onWake(%this) +{ + +} + +function classPrototyping::onSleep(%this) +{ + +} +//PrototypeClass(MainMenuGui) +//PrototypeClass(GuiChunkedBitmapCtrl) +function PrototypeClass(%classInstance) +{ + if (!isObject(%classInstance)) return; + Canvas.pushDialog(classPrototyping); + classPrototyping.fillClasslist(%classInstance); + classPrototyping.SetNamespaceUsed(%classInstance); +} + +function classPrototyping::fillClasslist(%this, %classInstance) +{ + ClassInheritanceListCtrl.deleteAllObjects(); + %this.instanceName = %classInstance.getName(); + + //get potentially scripted namespaces + %class = %classInstance.getClassName(); + %prepend = ""; + if (%classInstance.getName() !$= "") + %prepend = %classInstance.getName(); + if (%classInstance.class !$= "") + %prepend = %prepend SPC %classInstance.class; + if (%classInstance.superclass !$= "") + %prepend = %prepend SPC %classInstance.superclass; + + //append to hardcoded potential namespaces + %this.classlist = %prepend SPC getClassHierarchy(%class); + %this.classCount = getWordCount(%this.classlist); + for (%i=0; %i<%this.classCount; %i++) + { + %inheritanceOrder = %this.classCount-(%i+1); + %className = getWord(%this.classlist,%inheritanceOrder); + if (%i<%this.classCount-1) + %className = %className @"->"; + %elemClass = new GuiRadioCtrl("ProtoClassSelect"@ %i) { + text = %className; + entry = strreplace(%className,"->",""); + groupNum = "1"; + extent = "80 18"; + profile = "ToolsGuiRadioProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + eval("function ProtoClassSelect"@ %i @"::onClick(%this){classPrototyping.SetNamespaceUsed(%this.entry);}"); + ClassInheritanceListCtrl.addGuiControl(%elemClass); + } + %lastElem = "ProtoClassSelect"@ %this.classCount-1; + %lastElem.setStateOn(true); +} + +function classPrototyping::SetNamespaceUsed(%this, %nameSpaceUsed) +{ + ClassMethodListCtrl.deleteAllObjects(); + %this.fillMethodlist(%nameSpaceUsed); +} + +function classPrototyping::fillMethodlist(%this, %nameSpaceUsed) +{ + ClassMethodListCtrl.deleteAllObjects(); + %this.nameSpaceUsed = %nameSpaceUsed; + %this.methodArray = getMethodSigsNS(%nameSpaceUsed); + %this.methodCount = %this.methodArray.count(); + + for (%i=0; %i<%this.methodCount; %i++) + { + %methodDef = getRecord(%this.methodArray.getValue(%i),0); + %methodName = strreplace(%methodDef,"::"," "); + %methodName = getWord(strreplace(%methodName,"("," "),2); + + %elemMethod = new GuiCheckBoxCtrl("ProtoMethodSelect"@ %i) { + text = %methodName; + position = "1 1"; + profile = "ToolsGuiCheckBoxProfile"; + tooltipProfile = "GuiToolTipProfile"; + }; + ClassMethodListCtrl.addGuiControl(%elemMethod); + } +} + +function setProtoTypeFilePath(%targetPath) +{ + classPrototyping-->targetPath.text = %targetPath; +} + +function classPrototyping::readExistingLayout(%this) +{ + for (%i=0; %i<%this.classCount; %i++) + { + %inheritanceOrder = %this.classCount-(%i+1); + %obj = "ProtoClassSelect"@ %i; + if (%obj.isStateOn()) + %namespaceUsed = getWord(%this.classlist,%inheritanceOrder); + } + + %file = new FileObject(); + %filename = classPrototyping-->targetPath.text @"/"@ %namespaceUsed @"."@ $TorqueScriptFileExtension; + + if (!isObject(%this.callbacksDefined)) + %this.callbacksDefined = new arrayobject(); + %this.callbacksDefined.empty(); + %this.reportedCommands = false; + %this.reportedVariables = false; + %this.callbackBlockDefined = false; + + %key=0; + if(%file.openForRead(%filename)) + { + while (!%file.isEof()) + { + %line = %file.readLine(); + + //have we already reported commands? + if (startsWith(%line,"/* Available Commands:") ) + %this.reportedCommands = true; + + //have we already reported variables? + if (startsWith(%line,"/* HardCoded Variables") ) + %this.reportedVariables = true; + + if (startsWith(%line,"/*--- Callbacks ---*/") ) + %this.callbackBlockDefined = true; + + //get list of methods already existing + if (startswith(%line,"function "@ %namespaceUsed) ) + { + %methodName = strreplace(%line,"::"," "); + %methodName = getWord(strreplace(%methodName,"("," "),2); + %this.callbacksDefined.add(%key++,%methodName); + } + } + } + %file.delete(); +} + +function classPrototyping::writeResults(%this) +{ + %namespaceUsed = ""; + for (%i=0; %i<%this.classCount; %i++) + { + %inheritanceOrder = %this.classCount-(%i+1); + %obj = "ProtoClassSelect"@ %i; + if (%obj.isStateOn()) + %namespaceUsed = getWord(%this.classlist,%inheritanceOrder); + } + %this.readExistingLayout(); + %file = new FileObject(); + %filename = classPrototyping-->targetPath.text @"/"@ %namespaceUsed @"."@ $TorqueScriptFileExtension; + if(%file.openForAppend(%filename)) + { + if (ReportCommands.isStateOn() && %this.reportedCommands == false) + { + %this.commandArray = getMethodSigsNS(%this.nameSpaceUsed,true); + %this.commandCount = %this.commandArray.count(); + %file.writeLine("/* Available Commands:"); + for (%i=0; %i< %this.commandCount; %i++) + { + %file.writeLine(getRecord(%this.commandArray.getValue(%i),0)); + } + %file.writeLine("*/"); + } + + if (ReportVariables.isStateOn() && %this.reportedVariables == false) + { + %file.writeLine("/* HardCoded Variables"); + for (%i=0; %i< getFieldCountNS(%this.nameSpaceUsed); %i++) + { + %file.writeLine(getFieldNS(%this.nameSpaceUsed,%i)); + } + %file.writeLine("*/"); + } + + if (%this.callbackBlockDefined == false) + %file.writeLine("\n/*--- Callbacks ---*/\n"); + + for (%i=0; %i<%this.methodCount; %i++) + { + %obj = "ProtoMethodSelect"@ %i; + if (%obj.isStateOn()) + { + %methodDef = getRecord(%this.methodArray.getValue(%i),0); + %methodName = strreplace(%methodDef,"::"," "); + %methodName = getWord(strreplace(%methodName,"("," "),2); + if (%this.callbacksDefined.countValue(%methodName)==0) + { + echo(%methodName @ "not found. defining..."); + %file.writeLine("\n" @ strreplace(%this.methodArray.getValue(%i),%this.instanceName,%namespaceUsed)); + } + } + } + } + else + { + error( "Failed to open " @ %filename @ " for writing" ); + } + %file.delete(); +} \ No newline at end of file