GeneralRegarding FOSE Plugin Development

Post » Sat Feb 19, 2011 12:05 pm

For your information my main.cpp has the following header files included. These are not the minimum headers for the plugin like spotted in the

fose_example_plugin source. This is also includes relevant to some of my work.

#include "fose/PluginAPI.h"#include "fose/CommandTable.h"#include "fose/GameRTTI.h"#include "fose/GameAPI.h"#include "fose/ParamInfos.h"#include "fose/GameObjects.h"#include "fose/GameInterface.h"#include "fose/GameExtraData.h"


The header file CommandTable.h contains these lines

extern CommandTable	g_consoleCommands;extern CommandTable	g_scriptCommands;



How exactly do I access those two in my main.cpp?

Here is a sample of how I might use it:
CommandInfo* pCI = g_consoleCommands.GetStart();


It produces these linker errors

1>main.obj : error LNK2019: unresolved external symbol "public: struct CommandInfo * __thiscall CommandTable::GetStart(void)" (?GetStart@CommandTable@@QAEPAUCommandInfo@@XZ) referenced in function "int __cdecl FindGameCmd(char *,bool)" (?FindGameCmd@@YAHPAD_N@Z)1>main.obj : error LNK2001: unresolved external symbol "class CommandTable g_consoleCommands" (?g_consoleCommands@@3VCommandTable@@A)



My last problem is related to calling a script/console command of my own and most importantly an existing script/console command of the game.
I could not use the CommandTable due to the linker errors so I resorted to a work around.

int FindGameCmd(char* nzName, bool Console = false) {	CommandInfo* pCI = g_consoleCommands.GetStart();	UInt32 nCmdListBegin = CmdRange.Start;	UInt32 nCmdListEnd = CmdRange.End;	    CommandInfo* pCmdInfo = (CommandInfo*)nCmdListBegin;		int nCmdIndex = 0;	int nCmdCount = 300;	for(int iii = 0; iii < nCmdCount; ++iii)	{		if( iii >= nCmdCount) {			Console_Print("Search Limit Reached. Failed.");				break;		}		size_t found = std::string::npos;	 		std::string strCmdNameShort (pCmdInfo->shortName);		found = strCmdNameShort.find(nzName);		if(found != std::string::npos) {			nCmdIndex = iii;		    Console_Print("name: %s index: %d via: short-name", pCmdInfo->shortName, nCmdIndex);				break;		}		found = std::string::npos;		std::string strCmdNameLong (pCmdInfo->longName);		found = strCmdNameLong.find(nzName);		if (found != std::string::npos)		{			nCmdIndex = iii;		    Console_Print("name: %s index: %d via: long-name",  pCmdInfo->longName, nCmdIndex);				break;		}		   pCmdInfo = (CommandInfo*)(nCmdListBegin + (iii * sizeof(CommandInfo)));	  	}	return nCmdIndex-1;}


Later I show how I use the returned int (nCmdIndex) in my calculations to get a correct CommandInfo pointer for my target command.
Briefly I will share the basics of what I know.



From my understand there are at least 3 locations you will have in your source that relate to a custom command you make.

1. You Define it.

// Simple Example
DEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test", 0, 0, NULL);


// Example with requirement of a ref. DEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test. ref required. e.g. player.YourCommand", 1, 0, NULL);


The fourth parameter species how many arguments are expected.// Three ArgumentsDEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test. ref required. e.g. player.YourCommand", 1, 3, NULL);

Lastly if you noticed in the previous examples the last parameter is NULL. To set your ParamInfo you need a collection of ParamTypes. You can check with

the CommandTable.h for the ParamTypes already enumerated.
FOSE has common ParamInfos setup already. see CommandList.h


Example of a Custom
static ParamInfo kParams_PlaceAtMe[4] = {	{"ObjectID:ref", kParamType_TESObject, 0},	{"Count:int", kParamType_Integer,	   0},	{"Distance:float", kParamType_Float,   1},	{"Direction:int", kParamType_Integer,  1} // 1 = optional};// Usage. Notice the 4 matches the size of our PlaceAtMeDEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test", 0, 4,  kParams_PlaceAtMe);




2. The Guts
The guts of the function should be right after the ifdef #RUNTIME line and not after the corresponding #endif. However the DEFINITON is after the #endif

// Minimum needed inside your command .

bool Cmd_ExamplePlugin_Test_Execute(COMMAND_ARGS){*result = 0;return true;}


3. Register the CMD in the FOSEPlugin_Load

// register with a code-range in mind before releasing. The FOSE makers appreciate it if you let them know how many commands you need. The

OpCodeBase value may also be different
// depending on who else has already contacted FOSE makers for space.
	fose->SetOpcodeBase(0x2100);	fose->RegisterCommand(&kCommandInfo_ExamplePlugin_Test);	fose->RegisterCommand(&kCommandInfo_ExamplePlugin_TestOther);


Lastly I will state some things I am unclear in my understanding of.

COMMAND_ARGS contains a void* named arg1. There are some ExtractArg functions to retrieve the arguments. But those aren't good enough in my case. While I

can access the existing arguments using the Extraction method,
how would I add to the arguments?

First you may ask why would I want to? It is because the COMMAND_ARGS passed into my custom command are relevant to that command and not something like

PlaceAtMe. I could define my custom command to take the same arguments and paramInfo as PlaceAtMe but that isn't the point.


// I set a CommandInfo pointer to the address of my target function so that I can call execute();
CommandInfo* pCmdInfo = (CommandInfo*)(CmdRange.Start  + (nCmdIndex * + sizeof(CommandInfo)));// the PASS_COMMAND_ARGS takes everything in COMMAND_ARGS and passes it in.pCmdInfo->execute(PASS_COMMAND_ARGS);


The larger picture is simply this:bool Cmd_ExamplePlugin_Test_Execute(COMMAND_ARGS){int nCmdIndex = FindGameCmd("PlaceAtMe", true);CommandInfo* pCmdInfo = (CommandInfo*)(CmdRange.Start  + (nCmdIndex * + sizeof(CommandInfo)));paramInfo = kParams_PlaceAtMe;		// Again there is no PackArgs or ConstructArgs. The following byte array is important for placeatme to work.		BYTE baArgs [18]={0x01, 0x20, 0x0A, 0x00, 0x02, 0x00, 0x72, 0x01, 0x00, 0x6E,			0x01, 0x00, 0x00, 0x00, // count?			0x00, 0x00, 0x00, 0x00};// Another thing I don't understand is why references/TESObjects aren't part of the arguments in arg1. // Also why can't we just pass in a pointer to a Struct matching the format of kParams for the args?arg1 = &baArgs;// Depending on your custom command the scriptObj's number of references could be less than two.// However for PlaceAtMe two references are involved. One reference is the object we are placing at. The second reference is the object we are placing.scriptObj->info.numRefs = 2;// varItemBase will be our toPlace reference variable. // varItemBase  may need to be moved out of your custom command's scope.Script::RefVariable varItemBase;// pDupeForm can be swapped withvarItemBase.form = pDupeForm;//varIdx 0 is the first reference id variable. the reference your mouse has highlighted would be this .// varIdx 1 is the reference id you want to placevarItemBase.varIdx = 1;scriptObj->refs.AddAt(&varItemBase, 1);pCmdInfo->execute(PASS_COMMAND_ARGS);return true;}



Phew that was a lot. So with a for loop, how exactly would I call execute x amount of times, and everytime actually work like the first?
User avatar
Cheville Thompson
 
Posts: 3404
Joined: Sun Mar 25, 2007 2:33 pm

Return to Fallout 3