PalForge
Guides

Console commands

The pf_* actions, what each one needs on screen, and the three ways to run one

What you can do after this page

  • Run any PalForge measurement without hunting for a free key
  • Find out which command needs a world, a pal, or the title screen before you press anything
  • Turn UE4SS's console on, and recognise the case where it registered into a window that is not there
  • Run the same commands from a text file when there is no console and no key

Where the commands come from

Every command is an entry in palforge.test's ACTIONS table. installCommands registers one console command per entry and then prints the list, sorted, at every startup — so the log line is generated from the table it describes and cannot drift from it.

console commands: pf_hook  pf_hook_audio_custom_file_loader  pf_hook_audio_setvolume_audible ...

Three things follow from that:

  • The names in this page are what the table held when it was written. The log line is the authority. Grep UE4SS.log for console commands:.
  • A command and a key are two ways into the same function. pf_reflect and F5 run the same probe.
  • Nothing here exists unless env.dev is true. The whole palforge.test module is only required under the dev gate, and tools/deploy.sh --release deletes palforge/test/ from the deployed tree outright — so a release copy has no pf_* commands, no F1 and no probe keys, and the kernel's one require answers absent.

The body of a command is queued onto the game thread through ExecuteInGameThread, because everything it touches is a live UObject, and it is wrapped in a pcall — a command that raises would otherwise take UE4SS's whole console handler down with it.

Turning the console on

UE4SS ships with its console switched off, and registering a command is not the same as being able to type one. The handler registers perfectly well into a window that does not exist, and the log says console commands: ... while there is nowhere to put them.

ue4ss/UE4SS-settings.ini
ConsoleEnabled = 1
GuiConsoleEnabled = 1
GuiConsoleVisible = 1

Restart the game after editing it. PalForge cannot read that file, so it says this as a note rather than checking it.

The list

Forty-two actions, in four groups: eighteen of them always, plus one per declared hook — another twenty-five — once env.debug is on.

CommandWhat it does
pf_testsrun every suite the module owns, and announce the summary on screen
pf_keysprint Palworld's whole live key config, plus a free/game/palforge status per bindable name
pf_nativeread a named handle out of each native catalog and ask the game for its icon
pf_spawnspawn a pal beside you, then report 10 s later what is actually nearby
pf_teachgive the nearest pal a passive skill
pf_meshresolve every catalogued asset path, then hang a shipped chest in front of you for 30 s
pf_uideclmount a declared panel into the game's own UI root and count clicks on it
pf_uirouteread what Palworld itself does with UI, input mode and Esc, then watch it happen
pf_uizthree panels at once: z-order, key routing, the mouse half and the layer route
pf_reflect pf_pal pf_watch pf_title pf_uislot pf_uieventsthe six discovery probes, one per key
pf_hooks pf_hook <id> pf_hooks_allthe game-required test hooks (see below)
pf_hook_<id>one generated name per declared hook, twenty-five of them

Output that is meant to be pasted somewhere is bracketed, so it can be lifted straight out of the log:

#### BEGIN keymap
... every line the probe printed ...
#### END keymap

What each one needs on screen

A command that needs something it cannot find says so and stops. It never runs half of itself quietly.

CommandNeeds
pf_testsnothing, but a green run is two runs — once in a save and once at the title screen
pf_keysa loaded world: the key config lives on a world subsystem
pf_nativea loaded world, for the icon DataTable reads
pf_spawna loaded world; the pal arrives four to eight seconds later
pf_teacha pal standing near you — run pf_spawn first and wait
pf_mesha player pawn; it attaches a component of its own and destroys it again at 30 s
pf_uidecl pf_uiza loaded world, and someone to click the button
pf_uiroutea loaded world, then open the inventory and press Esc twice
pf_reflect pf_uislota loaded save
pf_pala pal standing near you
pf_watcha loaded save, then craft, drop or spawn something
pf_titlethe title screen
pf_uieventsa loaded save, then a quit to title and a reload

pf_uiroute and pf_uievents arm native hooks, and UE4SS cannot unregister one. They stay for the rest of the session. Every handler they arm is a counter plus one guarded string read, capped at the first few dozen samples.

Several of these keep watching after the command returns, through core/pollpf_mesh for 30 s, pf_uidecl for 75 s, pf_uiz for 110 s. While one is running, F9 refuses to reload and names the poller it is waiting for. That is the async guard doing its job; see Reload, poll and autorun.

The game-required hooks

Twenty-four measurements cannot be taken with the game switched off. Each is a declared hook under test/hooks/, naming what it needs on screen, whether it writes into a save, and which open item it closes. They load only when env.debug is true.

Scripts/palforge_dev.lua
local env = require("palforge.env")
env.dev   = true
env.debug = true                              -- loads test/hooks and generates its actions
env.debugHooks["mesh-color-change"] = true    -- and this one WRITES, so it needs its own switch

Three gates, and a closed one is printed rather than skipped:

  1. env.debug — without it no hook file is loaded at all.
  2. needs = { world, player, pal, title } — what has to be true in the running game.
  3. writes = true — additionally needs env.debugHooks[<id>] = true, because a session-wide switch is the wrong granularity for something that mutates a save.

Run pf_hooks first, always: it lists every hook, whether its gate is open, and the exact sentence that opens a shut one. It needs nothing but a loaded world.

Running one with no console at all

Three input routes have failed in turn on a real machine, and each time the work was fine and only the way in was missing: a probe on Palworld's own volume key (F7) bound successfully and the key never arrived; a second key was already taken; and the console ships switched off.

Scripts/palforge/autorun.txt is the route that needs neither. It runs once per world load, on world.ready.

Scripts/palforge/autorun.txt
# comments and blank lines are ignored
pf_native            # run as soon as the world is ready
8 pf_keys            # run 8 seconds after the world is ready
40 pf_hook_pal_skills_equip

A line is [delay] name and carries no argument. That is why pf_hook <id> is a console-only spelling and why one pf_hook_<id> name is generated per declared hook instead — one less thing a text file on disk can say to the game.

The delay matters more than it looks. A pal takes four to eight seconds to arrive, so an action that needs one nearby cannot run in the same breath as the spawn that makes it.

The file holds names, never code. core/autorun.lua names nothing under test/: the table is HANDED to it by test/init.lua's install(), through autorun.setActions(M.ACTIONS). Each line is a lookup in that table, and a name that is not there is reported and skipped:

-- core/autorun.lua, with the table it was handed
local run = M._actions[name]        -- set once by test/init.lua's install()
if not run then log.warn(name .. ": no action of that name") end

With no test tree — a release build — there is simply no table, and the queue is a no-op that costs nothing rather than a failed require on every world load.

So a stray file cannot execute anything a keypress could not, and a hook that writes still has its own second gate on top.

Summary

  • Every measurement is an entry in palforge.test's ACTIONS; the startup log line is the authoritative list.
  • Nothing exists without env.dev, and the twenty-five game-required hooks also need env.debug.
  • UE4SS's console is off by default — three settings and a restart.
  • Each command says what it needs on screen and stops rather than half-running.
  • pf_hooks before any hook: it prints every gate and the sentence that opens a shut one.
  • autorun.txt runs the same names with no key and no console, one [delay] name per line.

On this page