Communicate easily between Papyrus and Skyrim Platform
Skyrim Platform is lovely.
But communication between Papyrus and Skyrim Platform is not easy.
This makes it really easy!
Download the SkyrimPlatformPapyrusBridge
using your favorite mod manager:
Install the @skyrim-platform/papyrus-bridge
library in your Skyrim Platform plugin:
npm i @skyrim-platform/papyrus-bridge
A common use-case nowadays for using Papyrus alongside Skyrim Platform is:
- Using Papyrus to capture Papyrus events.
In this example, we'll capture a keyboard event and send it to Skyrim Platform.
💡 As of 2021, Skyrim Platform supports listening for Papyrus Events triggered on objects in the game, but it is very slow and not recommended at this time.
Name it something like HelloBridge.esp
I'm assuming you know how to create a new Quest and setup a Quest Alias pointing to the PlayerRef.
If not, see this video: Attaching Skyrim scripts to player events
Add an attached script to the PlayerRef alias, e.g. HelloBridge
We'll be using SkyrimPlatformConnection
which is a base script used to simplify communication between your mod's Papyrus and your mod's TypeScript.
Later, I'll add some documentation demonstrating using the raw
SkyrimPlatformBridge
Papyrus script which can be used globally from any Papyrus Script (not recommended)
Update HelloBridge.psc
with the following code:
scriptName HelloBridge extends SkyrimPlatformConnection
; Let's say we will do something when you press
; Left Shift + B
int LEFT_SHIFT_KEY = 42
int B_KEY = 48
event OnSetup()
; The 'Connection Name' is used to establish a connection
; between Papyrus and Skyrim Platform.
;
; Manually configuring this is optional.
; Default: Name of the Papyrus script, e.g. HelloBridge
ConnectionName = "HelloBridge"
endEvent
event OnConnected()
; Do something when connection to Skyrim Platform is established
RegisterForKey(B_KEY)
endEvent
event OnKeyDown(int keyCode)
if keyCode == B_KEY && Input.IsKeyPressed(LEFT_SHIFT_KEY)
; Tell Skyrim Platform about the key press
;
; This sends an event.
; You can provide an event name and optionally an additional string of data.
; By default the event is sent to the "HelloBridge" connection,
; but you can specify a target parameter to target a different connection.
Send("Keyboard Shortcut Pressed")
; In addition to 'Send' events (which do not return a response)
; you can use 'Request' to actually get a response from SkyrimPlatform
; e.g. Request("Name of some query")
endIf
endEvent
Add the following to a new or existing .ts
script in your plugin:
import { getConnection } from 'papyrusBridge'
import { once, Debug } from 'skyrimPlatform'
// Get a connection to your Papyrus code.
// The connection name should be the same here as in Papyrus.
const connection = getConnection('HelloBridge')
connection.onEvent((event) => {
// NOTE! Event names from papyrus.onEvent are ALWAYS LOWERCASE
if (event.eventName == 'keyboard shortcut pressed') {
// Note: onEvent can be called in contexts where `Debug.messageBox` does not work.
// You can use `once('update')` to be able to `Debug.messageBox`
once('update', () => {
Debug.messageBox('The keyboard shortcut was pressed!')
})
}
})
// Example of replying to Request() calls from Papyrus:
// connection.onRequest((request, reply) => {
// if (request.query == 'this will be lowercase')
// reply("Something")
// else
// reply("Something else")
// })
💡 If
'papyrusBridge'
does not autocomplete or your script does not compile, copy thePlatform\Modules\papyrusBridge.ts
file from the downloaded mod toSkyrim Special Edition\Data\Platform\Modules\
Run the game and press Left Shift + B
You should see the messagebox:
The keyboard shortcut was pressed!
SkyrimPlatformPapyrusBridge
has 2 main concepts for communication:
- Events: Fire & Forget! These send off a message from SP <--> Papyrus
- Requests: Get a response! These allow you to get responses from SP <--> Papyrus
You'll see in the documentation below that both Skyrim Platform and Papyrus provide similar functions for sending/receiving Events and Requests.
All event names and request queries will be lowercase when read by Skyrim Platform.
This is because Papyrus is case insensitive and sometimes sends inconsistently-cased event names and request queries.
Use getConnection
to get an instance of PapyrusBridge
which is configured to communicate with an instance of SkyrimPlatformConnection
on the Papyrus side of things.
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
Event triggered when the Papyrus connection successfully connects with the Skyrim Platform connection
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
connection.onConnected(() => {
// Do something
})
If you want to listen to ALL events which are sent to ANY Skyrim Platform connection, you can register callbacks on the papyrusBridge
object:
import papyrusBridge, { getConnection } from 'papyrusBridge'
// Regular connection code
const connection = getConnection('MyMod')
connection.onConnected(() => {
// Do something
})
// Listen for events on ANY connection:
papyrusBridge.onConnection(connectionName => {
// the connectionName is the connection that is now connected
})
papyrusBridge.onEvent(event => {
// you can check event.target to get the name of the connection which this
// event is intended for (or event.source to get the name of the connection)
// this event originated from
})
papyrusBridge.onRequest(request => {
// you can check request.target to get the name of the connection which this
// event is intended for (or request.source to get the name of the connection)
// this event originated from
})
Received events send by Papyrus via Send()
.
Events contain eventName
and data
properties.
❗ IMPORTANT: The
eventName
will ALWAYS BE IN LOWERCASE.
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
connection.onEvent((event) => {
switch (event.eventName) {
case 'myEvent': {
// do something with event.data
break
}
}
})
Received requests send by Papryrus via Request()
You should reply()
to these requests!
Events contain query
and data
properties.
❗ IMPORTANT: The
query
will ALWAYS BE IN LOWERCASE.
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
connection.onRequest((request, reply) => {
switch (event.query) {
case 'playerName': {
// Optionally do something with event.data
//
// Using once('update') here to get a context
// in which `Game.getPlayer()` can be used
once('update', () => {
reply(Game.getPlayer()?.getBaseObject()?.getName())
})
break
}
default {
reply('You should always reply with something')
break
}
}
})
Sends an event to Papyrus.
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
connection.onConnected(() => {
connection.send('someEventName', '[optional data]')
})
To receive a
send()
event on Papyrus side:event OnEvent(string eventName, string data) Debug.MessageBox("Received event!") endEvent
Makes an asynchronous request to Papyrus to get a response.
import { getConnection } from 'papyrusBridge'
const connection = getConnection('MyMod')
connection.onConnected(async () => {
const response = await connection.request('some/query', '[optional data]')
// optionally do something with response.data
once('update', () => {
Debug.messageBox(`Response for some/query: ${response.data}`)
})
})
To response to a
request()
on Papyrus side:event OnRequest(string replyId, string query, string data) Reply(replyId, "This is the response to this query") endEvent
To implement a script which is connected to Skyrim Platform, make an Alias and have it extend SkyrimPlatformConnection
scriptName MyConnection extends SkyrimPlatformConnection
You should NOT override these events:
OnInit()
OnPlayerLoadGame()
Instead, see OnSetup
below
Whenever this connection Alias is initialized -OR- the game is reloaded, OnSetup()
is invoked.
You can use this to perform any script setup as well as providing a manual configuration for your ConnectionName
scriptName MyScript extends SkyrimPlatformConnection
event OnSetup()
ConnectionName = "SomeConnectionName"
endEvent
By default, ConnectionName
defaults to the name of the script, e.g. MyScript
in the example above.
Event executed when a Papyrus connection has successfully connected to its Skyrim Platform counterpart.
This will never fire if you do not getConnection('TheConnectioName')
from Skyrim Platform.
event OnEvent(string eventName, string data)
Debug.MessageBox("Received event!")
endEvent
event OnRequest(string replyId, string query, string data)
Reply(replyId, "This is the response to this query")
endEvent
event OnRequest(string replyId, string query, string data)
Reply(replyId, "This is the response to this query")
endEvent
event OnConnected()
Send("Some Event Name", "[Optional Data Parameter]")
endEvent
event OnConnected()
string response = Request("Some Query Name", "[Optional Data Parameter]")
Debug.MessageBox("Received response: " + response)
endEvent