<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://wiki.shroudforge.net/index.php?action=history&amp;feed=atom&amp;title=Modding%2Fen-gb</id>
	<title>Modding/en-gb - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.shroudforge.net/index.php?action=history&amp;feed=atom&amp;title=Modding%2Fen-gb"/>
	<link rel="alternate" type="text/html" href="https://wiki.shroudforge.net/index.php?title=Modding/en-gb&amp;action=history"/>
	<updated>2026-11-07T23:23:22Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://wiki.shroudforge.net/index.php?title=Modding/en-gb&amp;diff=341&amp;oldid=prev</id>
		<title>FuzzyBot: Updating to match new version of source page</title>
		<link rel="alternate" type="text/html" href="https://wiki.shroudforge.net/index.php?title=Modding/en-gb&amp;diff=341&amp;oldid=prev"/>
		<updated>2026-11-06T21:38:46Z</updated>

		<summary type="html">&lt;p&gt;Updating to match new version of source page&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;languages /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- ShroudForge Modloader Wiki Post&lt;br /&gt;
title: Modding&lt;br /&gt;
description: 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.&lt;br /&gt;
language: en-GB&lt;br /&gt;
picture:ShroudForge-Wiki-modding.svg&lt;br /&gt;
creator: Bonsaibauer&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:ShroudForge-Wiki-modding.svg|thumb|left|600px|Modding]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear:both;&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
Enable the mod and declare the &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; capability in &amp;lt;code&amp;gt;mod.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;capabilities&amp;quot;: [&amp;quot;runtime&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For these client queries, set &amp;lt;code&amp;gt;targets&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;amp;#91;&amp;quot;client&amp;quot;&amp;amp;#93;&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;extended.mod.json&amp;lt;/code&amp;gt;. Call the functions from active runtime callbacks such as &amp;lt;code&amp;gt;on_load&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;on_update&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;runtime.phase == &amp;quot;ingame&amp;quot;&amp;lt;/code&amp;gt; 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.&lt;br /&gt;
&lt;br /&gt;
== Detect the main menu ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local in_menu, reason = runtime.is_main_menu()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;true, nil&amp;lt;/code&amp;gt; || The main menu, including character, world and server selection, has been confirmed.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;false, nil&amp;lt;/code&amp;gt; || A different original game state has been validated.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nil, reason&amp;lt;/code&amp;gt; || The state is unknown, changing, or the query is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Check &amp;lt;code&amp;gt;in_menu == true&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;in_menu == false&amp;lt;/code&amp;gt; explicitly. &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; is not a confirmed state. &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; alone does not confirm a readable world either. Check the operation you need separately, such as &amp;lt;code&amp;gt;runtime.has(&amp;quot;runtime.ecs.read&amp;quot;)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Read the character name ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local name, reason = runtime.player.name()&lt;br /&gt;
local display_name = name or &amp;quot;Flameborn&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;name, nil&amp;lt;/code&amp;gt; returns the character name selected in the local main menu as a UTF-8 string. Unicode characters, as in &amp;lt;code&amp;gt;Ikiju 育樹&amp;lt;/code&amp;gt;, are preserved. &amp;lt;code&amp;gt;nil, reason&amp;lt;/code&amp;gt; means that no name is available yet, or access is unavailable or not allowed.&lt;br /&gt;
&lt;br /&gt;
The mod chooses its own fallback name. The API inserts no default and does not use the Modloader&amp;#039;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.&lt;br /&gt;
&lt;br /&gt;
== Shared cache ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The last successfully captured name remains cached after joining a world. A cached name therefore does not confirm that the menu is open. &amp;lt;code&amp;gt;runtime.has(&amp;quot;runtime.is_main_menu&amp;quot;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;runtime.has(&amp;quot;runtime.player.name&amp;quot;)&amp;lt;/code&amp;gt; report each query&amp;#039;s availability. &amp;lt;code&amp;gt;runtime.status(...)&amp;lt;/code&amp;gt; also supplies a reason.&lt;br /&gt;
&lt;br /&gt;
== Example for &amp;lt;code&amp;gt;src/mod.lua&amp;lt;/code&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
This example reads the cache on load and every 500 ms afterwards. It chooses a fallback name and logs only changes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
runtime.require(&amp;quot;runtime.lifecycle&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local previous_message = nil&lt;br /&gt;
&lt;br /&gt;
local function refresh()&lt;br /&gt;
    local in_menu = runtime.is_main_menu()&lt;br /&gt;
    local name = runtime.player.name()&lt;br /&gt;
    local state = &amp;quot;unknown&amp;quot;&lt;br /&gt;
    if in_menu == true then&lt;br /&gt;
        state = &amp;quot;menu&amp;quot;&lt;br /&gt;
    elseif in_menu == false then&lt;br /&gt;
        state = &amp;quot;other&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local message = state .. &amp;quot;: &amp;quot; .. (name or &amp;quot;Flameborn&amp;quot;)&lt;br /&gt;
    if message ~= previous_message then&lt;br /&gt;
        previous_message = message&lt;br /&gt;
        shroudforge.log.info(message)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    on_load = refresh,&lt;br /&gt;
    on_update = function()&lt;br /&gt;
        refresh()&lt;br /&gt;
    end,&lt;br /&gt;
    update_interval_ms = 500,&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:guide:Shroudforge]]&lt;/div&gt;</summary>
		<author><name>FuzzyBot</name></author>
	</entry>
</feed>