Jump to content

Can someone explain SKSE plugins to me?!


BigKevSexyMan

Recommended Posts

I did my best to try to fully understand them from the example in skse, but it's just not registering with me. I know c++ and am fairly proficient in it, but I just can pull how to start out of the example. Can anyone help me by telling me a bit more about what an skse plugin does and maybe point me to a few other examples I could look at?

 

Basically, I'm trying to make a new papyrus command that I pass a string to and then it performs an action outside of skyrim. I go into more detail on what exactly it is in this thread. http://forums.nexusmods.com/index.php?/topic/846389-diablo-style-hardcore-mode/page__p__6783527

 

pseudocode example:

startcommand(string playername)
   get_savegame_path()
   check_savegames_for_playername()
   delete_savegame

Link to comment
Share on other sites

Well, I tried to take a look at SkyUI, but it doesn't use skse plugins. And Uncapper looked promising until I found out I couldn't disassemble the .dll plugin that it had. Still thanks for the help.

 

I took a deeper look at the plugin example that came with skse, and I understand it a little better, but there's this #ifdef _PPAPI variable that's throwing me for some problems. I'm trying to look into that a little more.

Link to comment
Share on other sites

  • 2 weeks later...

I did like going through that, but unfortunately it doesn't contain anything that adds to papyrus. I've done a lot of narrowing things down, and I found the line that's really giving me trouble:

 

registry->RegisterFunction(
	new NativeFunction0<StaticFunctionTag, UInt32>("TestValue", "PapyrusExample", TestPapyrus::TestValue, registry));

 

When I my code contains this line I get 4 LNK2019 errors in visual studio. I don't get why this doesn't work, I'm opening this project up directly from the skse download. I've search the internet, and what little there is out there on skse and this problem isn't coming up.

 

Here's my source code, maybe someone will see what's happening. Although the source code might not be the problem so much as it would be my project settings. But again, they should all be skse default.

 

TestPapyrus.h

#pragma once

#include "skse/GameTypes.h"

struct StaticFunctionTag;
class VMClassRegistry;

namespace TestPapyrus
{
void RegisterFuncs(VMClassRegistry* registry);

UInt32 TestValue(StaticFunctionTag*);

UInt32 asdf(UInt32 a);

};

 

TestPapyrus.cpp

#include "TestPapyrus.h"

#include "skse/GameObjects.h"

UInt32 TestPapyrus::TestValue(void*)
{
return 12345;
}

UInt32 TestPapyrus::asdf(UInt32 a)
{
return 54321;
}

#include "skse/PapyrusVM.h"
#include "skse/PapyrusNativeFunctions.h"

void TestPapyrus::RegisterFuncs(VMClassRegistry* registry)
{
registry->RegisterFunction(
	new NativeFunction0<StaticFunctionTag, UInt32>("TestValue", "PapyrusExample", TestPapyrus::TestValue, registry));


}

 

main.cpp (with most papyrus code taken out)


//#define _PPAPI 1

#include "skse/PluginAPI.h"
#include "skse/skse_version.h"
#include "skse/SafeWrite.h"
#include "skse/ScaleformCallbacks.h"
#include "skse/ScaleformMovie.h"
#include "skse/GameAPI.h"


IDebugLog	gLog("skse_example_plugin.log");

PluginHandle	g_pluginHandle = kPluginHandle_Invalid;

#ifdef _PPAPI
SKSEPapyrusInterface * papyrus = NULL;
#endif


extern "C"
{

bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info)
{
	_MESSAGE("skse_example_plugin");

	// populate info structure
	info->infoVersion =	PluginInfo::kInfoVersion;
	info->name =		"example plugin";
	info->version =		1;

	// store plugin handle so we can identify ourselves later
	g_pluginHandle = skse->GetPluginHandle();

	if(skse->isEditor)
	{
		_MESSAGE("loaded in editor, marking as incompatible");

		return false;
	}
	else if(skse->runtimeVersion != RUNTIME_VERSION_1_8_151_0)
	{
		_MESSAGE("unsupported runtime version %08X", skse->runtimeVersion);

		return false;
	}

#ifdef _PPAPI
	papyrus = (SKSEPapyrusInterface *)skse->QueryInterface(kInterface_Papyrus);
	if(!papyrus)
	{
		_MESSAGE("couldn't get papyrus interface");
		return false;
	}

	if(papyrus->interfaceVersion < SKSEPapyrusInterface::kInterfaceVersion)
	{
		_MESSAGE("papyrus interface too old (%d expected %d)", papyrus->interfaceVersion, SKSEPapyrusInterface::kInterfaceVersion);
		return false;
	}
#endif
	// ### do not do anything else in this callback
	// ### only fill out PluginInfo and return true/false

	// supported runtime version
	return true;
}

bool SKSEPlugin_Load(const SKSEInterface * skse)
{
	_MESSAGE("load");

#ifdef _PPAPIg
	papyrus->Register(RegisterPapyrus);
#endif

	return true;
}

};

 

Finally, here are my Linker Errors

 

Error 2 error LNK2019: unresolved external symbol "void __cdecl PackValue<unsigned long>(class VMValue *,unsigned long *,class VMClassRegistry *)" (??$PackValue@K@@YAXPAVVMValue@@PAKPAVVMClassRegistry@@@Z) referenced in function "public: virtual bool __thiscall NativeFunction0<struct StaticFunctionTag,unsigned long>::Run(class VMValue *,class VMClassRegistry *,unsigned long,class VMValue *,class VMState *)" (?Run@?$NativeFunction0@UStaticFunctionTag@@K@@UAE_NPAVVMValue@@PAVVMClassRegistry@@K0PAVVMState@@@Z) C:\Users\BKSM\Desktop\skse_1_06_05\src\skse\plugin_example\TestPapyrus.obj plugin_example

 

Error 3 error LNK2019: unresolved external symbol "void * __cdecl UnpackHandle(class VMValue *,unsigned long)" (?UnpackHandle@@YAPAXPAVVMValue@@K@Z) referenced in function "void __cdecl UnpackValue<struct StaticFunctionTag>(struct StaticFunctionTag * *,class VMValue *)" (??$UnpackValue@UStaticFunctionTag@@@@YAXPAPAUStaticFunctionTag@@PAVVMValue@@@Z) C:\Users\BKSM\Desktop\skse_1_06_05\src\skse\plugin_example\TestPapyrus.obj plugin_example

 

Error 4 error LNK2019: unresolved external symbol "unsigned long __cdecl GetTypeID<unsigned long>(class VMClassRegistry *)" (??$GetTypeID@K@@YAKPAVVMClassRegistry@@@Z) referenced in function "public: __thiscall NativeFunction0<struct StaticFunctionTag,unsigned long>::NativeFunction0<struct StaticFunctionTag,unsigned long>(char const *,char const *,unsigned long (__cdecl*)(struct StaticFunctionTag *),class VMClassRegistry *)" (??0?$NativeFunction0@UStaticFunctionTag@@K@@QAE@PBD0P6AKPAUStaticFunctionTag@@@ZPAVVMClassRegistry@@@Z) C:\Users\BKSM\Desktop\skse_1_06_05\src\skse\plugin_example\TestPapyrus.obj plugin_example

 

Error 5 error LNK2019: unresolved external symbol "public: void __thiscall NativeFunction::DebugRunHook(class VMValue *,class VMClassRegistry *,unsigned long,class VMValue *,class VMState *)" (?DebugRunHook@NativeFunction@@QAEXPAVVMValue@@PAVVMClassRegistry@@K0PAVVMState@@@Z) referenced in function "public: virtual bool __thiscall NativeFunction0<struct StaticFunctionTag,unsigned long>::Run(class VMValue *,class VMClassRegistry *,unsigned long,class VMValue *,class VMState *)" (?Run@?$NativeFunction0@UStaticFunctionTag@@K@@UAE_NPAVVMValue@@PAVVMClassRegistry@@K0PAVVMState@@@Z) C:\Users\BKSM\Desktop\skse_1_06_05\src\skse\plugin_example\TestPapyrus.obj plugin_example

 

Error 6 error LNK1120: 4 unresolved externals C:\Users\BKSM\Desktop\skse_1_06_05\src\skse\Debug\plugin_example.dll plugin_example

Link to comment
Share on other sites

Its been so long since I played with this but Ill take a guess or two anyway. :confused:

 

I remember getting warnings about breaking the version control during project conversion but thats no big deal. iirc the skse guys build with the full vs2008 but I use the 2008 and 2010 express editions. The project was ready to go after conversion for me on both editions but its still one thing to think about.

 

And.. Are you trying to build just your plugin or did you build skse first.

Link to comment
Share on other sites

Actually, I ended up emailing them, and they replied that the papyrus interface for skse isn't available for plugins yet. They also suggested I copy a few classes from skse into my project to get rid of the linker errors I mentioned, and it compiled, but my function in game didn't run. Like they said. I guess I'll just keep an eye out for the next version of skse, and hope they added the feature.

 

But I promise this, when they come out with the version that allows this, and I manage to figure it out, I'll post tutorials about what I figured out. Because this lack of tutorial stuff for skse is just frustrating.

 

I guess I'll just work on my other projects for now. But thanks scrivener07 for trying to help me out.

Link to comment
Share on other sites

  • 1 year later...
  • 3 weeks later...

Since this is the post that comes up when searching for this topic, and ragnaroklucifer already resurrected it, here are my findings with skse 1.0.7.0.0:

 

By default the #ifdef _PPAPI is commented out now, so you can make your own native functions without defining it (but the interface is still unsupported and subject to change in future releases of skse).

To make a native papyrus function you would need at minimum

#include "skse/PluginAPI.h"
#include "skse/PapyrusArgs.h"
#include "skse/PapyrusVM.h"
#include "skse/PapyrusNativeFunctions.h"

and to copy the corresponding .cpp files to your project (they come with the solution, but only the skse project has them and your plugin project cannot see the code it needs). You also need to copy PapyrusNativeFunctionDef.inl and PapyrusNativeFunctionDef_Base.inl to your project (also in project skse). This way you can avoid a) copying code and/or b) using #include on .cpp files (two bad ideas).

The skse solution has 5 projects in it to be clear on the distinction, so don't confuse the overall skse solution and the specific skse project. They are common_vc9, loader_common, skse, skse_loader, and steam_loader. It also comes with a plugin_example project to get you started. You actually only need to build common_vc9 and your plugin to create your plugin dll; you only need the other three projects for reference if you are not forking your own version of skse.

MyPlugin.h

#pragma once

#include "skse/PluginAPI.h"
#include "skse/PapyrusArgs.h"
#include "skse/PapyrusVM.h"
#include "skse/PapyrusNativeFunctions.h"

namespace MyPluginNS
{
    bool RegisterFuncs(VMClassRegistry* registry);

    float DoMathFun(StaticFunctionTag* base, float fIn1, float fIn2);
}

MyPlugin.cpp

#include "MyPlugin.h"

namespace MyPluginNS
{
    float DoMathFun(StaticFunctionTag* base, float fArg1, float fArg2)
    {
// your own custom function to be used in papyrus scripts
        return fArg1 + fArg2;
    }

    bool RegisterFuncs(VMClassRegistry* registry)
    {
// register the function with the game
// note you need to have the actual function name for the 3rd argument, but I am calling it
// "MyPlugin_DoMathFun" for the name it will be used as in papyrus scripts to avoid conflicts
// since you can't use the namespace in your scripts like we can in the plugin
        registry->RegisterFunction(
            new NativeFunction2 <StaticFunctionTag, float, float, float>("MyPlugin_DoMathFun", "Utility", MyPluginNS::DoMathFun, registry));

// example optional flag you can set
        registry->SetFunctionFlags("Utility", "MyPlugin_DoMathFun", VMClassRegistry::kFunctionFlag_NoWait);

        return true;
    }
}

main.cpp

#include "MyPlugin.h"

// here is a global reference to the interface, keeping to the skse style
SKSEPapyrusInterface        * g_papyrus = NULL;

// any other things the plugin does

extern "C"
{
// don't forget the plugin query, skipped here

// barebones load if your plugin is only registering papyrus functions,
// otherwise add other things to this
bool SKSEPlugin_Load(const SKSEInterface * skse)
{

// you need to get the papyrus interface being used by the main skse dll,
// otherwise skse won't know to ever register your functions
    g_papyrus = (SKSEPapyrusInterface *)skse->QueryInterface(kInterface_Papyrus);

// now register the function to register your functions with skse, so skse will
// know to add your plugins when it is registering all of its own custom functions
    bool btest = g_papyrus->Register(MyPluginNS::RegisterFuncs);

    if (btest)
        _MESSAGE("Register Succeeded"); //example error check

    return true;
}

}; // leaving extern "C"

You're not done. You still need to tell papyrus that these functions exist in order to use them in scripts, or they won't compile.

So in this case I used the "Utility" class of functions, so I need to open the latest skse copy of Utility.psc and add "MyPlugin_DoMathFun(StaticFunctionTag* base, float fArg1, float fArg2)" to the bottom and then re-compile it and put the .pex in my Data/Scripts folder. MyPlugin.dll needs to go in Data/SKSE/Plugins.

Now it should work to call "MyPlugin_DoMathFun(StaticFunctionTag* base, float fArg1, float fArg2)" in your scripts.

Anyone using your plugin would need both MyPlugin.dll and your Utility.pex. You should document the functions so that people can recompile the Utility.pex with your function headers anytime skse is update (or to merge with other mods) if you stop supporting the mod.

 

edit: You should probably also make sure g_papyrus isn't null before trying to register your function register callback in the plugin load code.

bool btest = false;
if (g_papyrus)
	btest = g_papyrus->Register(MyPluginNS::RegisterFuncs);
Link to comment
Share on other sites

  • 1 month later...

just in case this might help someone else :

 

first building my skse & skse plugin using visual studio express 2012 in debug mode, i was not able to make it run : tesv.exe was launched by skse loader, but will stick there forever.

 

I finally made my custom skse installation & plugin work by reverting to visual studio express 2008, AND building in release mode instead of debug mode.

I was then able to add my custom papyrus functions, fully usable from my scripts.

Edited by gira42
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...