Jump to content
Toggle menu
  • 3 articles
  • 22 files
  • 3 users
  • 2.5K edits
Shroudforge
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Modding: Difference between revisions

From Shroudforge
Create en-GB source page for translation
 
Marked this version for translation
 
Line 15: Line 15:
<translate>
<translate>


<!--T:1-->
Read the main-menu state and selected local character name through the Lua runtime. Both queries use the same native data as the Modloader and require no world access.
Read the main-menu state and selected local character name through the Lua runtime. Both queries use the same native data as the Modloader and require no world access.


== Prerequisites ==
== Prerequisites == <!--T:2-->


<!--T:3-->
Enable the mod and declare the <code>runtime</code> capability in <code>mod.json</code>:
Enable the mod and declare the <code>runtime</code> capability in <code>mod.json</code>:


<!--T:4-->
<pre>
<pre>
{
{
Line 27: Line 30:
</pre>
</pre>


<!--T:5-->
For these client queries, set <code>targets</code> to <code>&#91;"client"&#93;</code> in <code>extended.mod.json</code>. Call the functions from active runtime callbacks such as <code>on_load</code> and <code>on_update</code>. <code>runtime.phase == "ingame"</code> describes the started mod runtime and does not confirm entry into a world. Neither query needs an execution profile. Other world operations retain their own requirements.
For these client queries, set <code>targets</code> to <code>&#91;"client"&#93;</code> in <code>extended.mod.json</code>. Call the functions from active runtime callbacks such as <code>on_load</code> and <code>on_update</code>. <code>runtime.phase == "ingame"</code> describes the started mod runtime and does not confirm entry into a world. Neither query needs an execution profile. Other world operations retain their own requirements.


== Detect the main menu ==
== Detect the main menu == <!--T:6-->


<!--T:7-->
<pre>
<pre>
local in_menu, reason = runtime.is_main_menu()
local in_menu, reason = runtime.is_main_menu()
</pre>
</pre>


<!--T:8-->
{| class="wikitable"
{| class="wikitable"
! Return value !! Meaning
! Return value !! Meaning
Line 45: Line 51:
|}
|}


<!--T:9-->
Check <code>in_menu == true</code> or <code>in_menu == false</code> explicitly. <code>nil</code> is not a confirmed state. <code>false</code> alone does not confirm a readable world either. Check the operation you need separately, such as <code>runtime.has("runtime.ecs.read")</code>.
Check <code>in_menu == true</code> or <code>in_menu == false</code> explicitly. <code>nil</code> is not a confirmed state. <code>false</code> alone does not confirm a readable world either. Check the operation you need separately, such as <code>runtime.has("runtime.ecs.read")</code>.


== Read the character name ==
== Read the character name == <!--T:10-->


<!--T:11-->
<pre>
<pre>
local name, reason = runtime.player.name()
local name, reason = runtime.player.name()
Line 54: Line 62:
</pre>
</pre>


<!--T:12-->
<code>name, nil</code> returns the character name selected in the local main menu as a UTF-8 string. Unicode characters, as in <code>Ikiju 育樹</code>, are preserved. <code>nil, reason</code> means that no name is available yet, or access is unavailable or not allowed.
<code>name, nil</code> returns the character name selected in the local main menu as a UTF-8 string. Unicode characters, as in <code>Ikiju 育樹</code>, are preserved. <code>nil, reason</code> means that no name is available yet, or access is unavailable or not allowed.


<!--T:13-->
The mod chooses its own fallback name. The API inserts no default and does not use the Modloader's manual name selection. The value identifies no remote player and is neither a SteamID nor a Steam display name. A Dedicated Server has no local client character name.
The mod chooses its own fallback name. The API inserts no default and does not use the Modloader's manual name selection. The value identifies no remote player and is neither a SteamID nor a Steam display name. A Dedicated Server has no local client character name.


== Shared cache ==
== Shared cache == <!--T:14-->


<!--T:15-->
Lua and the Modloader read the same native cache. The existing runtime tick refreshes the menu state. The existing menu integration captures the name on the first available selection and again when selection or name metadata changes. Lua queries start no separate discovery and read no logs.
Lua and the Modloader read the same native cache. The existing runtime tick refreshes the menu state. The existing menu integration captures the name on the first available selection and again when selection or name metadata changes. Lua queries start no separate discovery and read no logs.


<!--T:16-->
The last successfully captured name remains cached after joining a world. A cached name therefore does not confirm that the menu is open. <code>runtime.has("runtime.is_main_menu")</code> and <code>runtime.has("runtime.player.name")</code> report each query's availability. <code>runtime.status(...)</code> also supplies a reason.
The last successfully captured name remains cached after joining a world. A cached name therefore does not confirm that the menu is open. <code>runtime.has("runtime.is_main_menu")</code> and <code>runtime.has("runtime.player.name")</code> report each query's availability. <code>runtime.status(...)</code> also supplies a reason.


== Example for <code>src/mod.lua</code> ==
== Example for <code>src/mod.lua</code> == <!--T:17-->


<!--T:18-->
This example reads the cache on load and every 500 ms afterwards. It chooses a fallback name and logs only changes:
This example reads the cache on load and every 500 ms afterwards. It chooses a fallback name and logs only changes:


<!--T:19-->
<pre>
<pre>
runtime.require("runtime.lifecycle")
runtime.require("runtime.lifecycle")


<!--T:20-->
local previous_message = nil
local previous_message = nil


<!--T:21-->
local function refresh()
local function refresh()
     local in_menu = runtime.is_main_menu()
     local in_menu = runtime.is_main_menu()
Line 83: Line 99:
     end
     end


     local message = state .. ": " .. (name or "Flameborn")
     <!--T:22-->
local message = state .. ": " .. (name or "Flameborn")
     if message ~= previous_message then
     if message ~= previous_message then
         previous_message = message
         previous_message = message
Line 90: Line 107:
end
end


<!--T:23-->
return {
return {
     on_load = refresh,
     on_load = refresh,

Latest revision as of 21:38, 6 November 2026


Modding



Read the main-menu state and selected local character name through the Lua runtime. Both queries use the same native data as the Modloader and require no world access.

Prerequisites

Enable the mod and declare the runtime capability in mod.json:

{
  "capabilities": ["runtime"]
}

For these client queries, set targets to ["client"] in extended.mod.json. Call the functions from active runtime callbacks such as on_load and on_update. runtime.phase == "ingame" describes the started mod runtime and does not confirm entry into a world. Neither query needs an execution profile. Other world operations retain their own requirements.

Detect the main menu

local in_menu, reason = runtime.is_main_menu()
Return value Meaning
true, nil The main menu, including character, world and server selection, has been confirmed.
false, nil A different original game state has been validated.
nil, reason The state is unknown, changing, or the query is unavailable.

Check in_menu == true or in_menu == false explicitly. nil is not a confirmed state. false alone does not confirm a readable world either. Check the operation you need separately, such as runtime.has("runtime.ecs.read").

Read the character name

local name, reason = runtime.player.name()
local display_name = name or "Flameborn"

name, nil returns the character name selected in the local main menu as a UTF-8 string. Unicode characters, as in Ikiju 育樹, are preserved. nil, reason means that no name is available yet, or access is unavailable or not allowed.

The mod chooses its own fallback name. The API inserts no default and does not use the Modloader's manual name selection. The value identifies no remote player and is neither a SteamID nor a Steam display name. A Dedicated Server has no local client character name.

Shared cache

Lua and the Modloader read the same native cache. The existing runtime tick refreshes the menu state. The existing menu integration captures the name on the first available selection and again when selection or name metadata changes. Lua queries start no separate discovery and read no logs.

The last successfully captured name remains cached after joining a world. A cached name therefore does not confirm that the menu is open. runtime.has("runtime.is_main_menu") and runtime.has("runtime.player.name") report each query's availability. runtime.status(...) also supplies a reason.

Example for src/mod.lua

This example reads the cache on load and every 500 ms afterwards. It chooses a fallback name and logs only changes:

runtime.require("runtime.lifecycle")

local previous_message = nil

local function refresh()
    local in_menu = runtime.is_main_menu()
    local name = runtime.player.name()
    local state = "unknown"
    if in_menu == true then
        state = "menu"
    elseif in_menu == false then
        state = "other"
    end

    local message = state .. ": " .. (name or "Flameborn")
    if message ~= previous_message then
        previous_message = message
        shroudforge.log.info(message)
    end
end

return {
    on_load = refresh,
    on_update = function()
        refresh()
    end,
    update_interval_ms = 500,
}