Skip to content

Latest commit

 

History

History
120 lines (98 loc) · 4.88 KB

File metadata and controls

120 lines (98 loc) · 4.88 KB

Papyrus调用

回到目录 | 工具配置 | 脚本说明 | 快速入门 | 插件基础 | Papyrus调用 | 事件响应 | 内存补丁 | 函数Hook

本节教程演示如何在插件注册一个原生函数(native)到游戏中供Papyrus脚本调用.

SKSE

函数原型

首先我们准备符合SKSE标准的Papyrus原生函数:

std::string GetText(RE::StaticFunctionTag*)
{
    return "Hello Papyrus"s;
}

std::string GetIntText(RE::StaticFunctionTag*, std::int32_t a_int)
{
    return fmt::format("Hello Papyrus, this is {}", a_int);
}

函数很简单, GetText()被Papyrus调用时, 返回一个内容为Hello Papyrus的字符串. 而GetIntText(Int)被调用时, 返回一个内容为Hello PapyrusInt参数转化的字符串. Papyrus原生函数在CLib中以RE::StaticFunctionTag*作为第一个参数, 即使这个函数本身并不接受参数. 类似于成员函数都以this作为第一个参数.

注册Papyrus虚拟机

有了Papyrus原生函数后, 便需要把这个函数注册到游戏内部的Papyrus虚拟机中, 这样我们的Papyrus脚本就能以此调用我们SKSE插件为其注册的函数. SKSE注册Papyrus函数分为三部分, 1) 获取SKSE提供的Papyrus接口; 2) 通过Papyrus接口注册Papyrus虚拟机处理函数; 3) 通过Papyrus虚拟机处理函数注册我们的Papyrus原生函数至Papyrus虚拟机内.
以下为示例代码:

namespace
{
	std::string GetText(RE::StaticFunctionTag*)
	{
		return "Hello Papyrus"s;
	}

	std::string GetIntText(RE::StaticFunctionTag*, std::uint32_t a_int)
	{
		return fmt::format("Hello Papyrus, this is {}", a_int);
	}

	// 3) 注册我们的Papyrus原生函数
	bool PapyrusVMHandler(RE::BSScript::IVirtualMachine* a_vm)
	{
		a_vm->RegisterFunction("SKSE_GetText", "MyNewPlugin_Native", GetText);
		a_vm->RegisterFunction("SKSE_GetIntText", "MyNewPlugin_Native", GetIntText);

		return true;
	}

	void MessageHandler(SKSE::MessagingInterface::Message* a_msg) noexcept
	{
		if (a_msg->type == SKSE::MessagingInterface::kDataLoaded) {
			// 1) 获取SKSE提供的Papyrus接口
			auto* papyrus = SKSE::GetPapyrusInterface();
			// 2) 注册Papyrus虚拟机处理函数
			papyrus->Register(PapyrusVMHandler);
		}
	}
}

DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_skse)
{
#ifndef NDEBUG
	while (!IsDebuggerPresent()) { Sleep(100); }
#endif

	DKUtil::Logger::Init(Plugin::NAME, REL::Module::get().version().string());
	SKSE::Init(a_skse);
	INFO("{} v{} loaded", Plugin::NAME, Plugin::Version);

	// do stuff
	if (!SKSE::GetMessagingInterface()->RegisterListener(MessageHandler)) {
		return false;
	}

	return true;
}

Papyrus虚拟机提供的RegisterFunction用于将一个C++原生函数注册到Papyrus虚拟机中, 并将其与Papyrus内对应的函数名字及类名字绑定.

// 将GetText注册为SKSE_GetText, 类名为MyNewPlugin_Native
a_vm->RegisterFunction("SKSE_GetText", "MyNewPlugin_Native", GetText);
// 将GetIntText注册为SKSE_GetIntText, 类名为MyNewPlugin_Native
a_vm->RegisterFunction("SKSE_GetIntText", "MyNewPlugin_Native", GetIntText);

调用Papyrus原生函数

在我们的MyNewPlugin_Native.psc中加入:

Scriptname MyNewPlugin_Native

String Function GetText() native
String Function GetIntText(Int aiNum) native

按照Papyrus脚本格式调用即可(Scriptname XXX extends MyNewPlugin_Native, native global等).

Papyrus类型与C++类型对照

一些常用的类型:

Papyrus C++
Int std::int32_t
Float float
String std::stringRE::BSFixedString
Bool bool
Actor RE::Actor*
Faction RE::TESFaction*
Form RE::TESForm*
ObjectReference RE::TESObjectREFR*
Quest RE::TESQuest*
ReferenceAlias RE::BGSRefAlias*
Shout RE::TESShout*
Spell RE::SpellItem*

当Papyrus类型为数组时, C++类型为编译期已知std::array或动态std::vector.
当Papyrus脚本传入数组参数时, C++类型为const RE::reference_array.


回到目录 | 工具配置 | 脚本说明 | 快速入门 | 插件基础 | Papyrus调用 | 事件响应 | 内存补丁 | 函数Hook