Flayer Function Attribute
The Flayer Function attribute system provides a clean, declarative way to expose game functionality to AI agents. It handles the registration, validation, and execution of functions that can be called remotely by AI agents over EldritchLink v5 (EL5).
How it works
Any static method marked with the [FlayerFunction] attribute is automatically discovered and registered as an EL5 RPC handler for every new connection. When the SDK receives a Call frame, it matches it with these registered handlers and invokes the appropriate function with the received arguments.
A [FlayerFunction] method must be static, or it will not appear in the
function list. The containing class does not have to be static.
For parameters and return types, only the supported EL5 value types are allowed to ensure reliable serialization across the network boundary (see Supported types).
The attribute also carries metadata that helps AI agents understand and use the function — most importantly the description, and optionally the hint key, which controls when the function becomes available. This metadata-driven approach lets agents make more informed decisions about when and how to call each function.
In 1.x, every Flayer function had to take a PacketLogger as its final
parameter. That is gone in 2.0. Use UnityEngine.Debug for diagnostics — logs
are forwarded automatically while connected.
Attribute Parameters
name(required): The name the function is registered under.description(required): A description of what the function does, for the AI.hintKey(optional): The hint key decides WHEN the function is exposed to the AI.exposed(optional, defaulttrue): Whether the function is exposed to the AI (only takes effect if the hint key matches).yieldReturnType(required forIEnumerator): The type a coroutine is expected to yield as its result.volatileFunction(optional): Whether the function can be overridden.true: Can be overridden by a non-volatile implementation.false(default): Cannot be overridden.
Argument metadata with [FlayerArgument]
Annotate parameters with [FlayerArgument] to give the agent a description and, where relevant, coordinate metadata. The Coordinate field accepts x, y, point, or point_list, which tells the agent that an argument maps to an on-screen position.
[FlayerFunction("set_move_target", "Moves the player to a viewport position")]
public static void SetMoveTarget(
[FlayerArgument("Horizontal viewport position", Coordinate = "x")] float x,
[FlayerArgument("Vertical viewport position", Coordinate = "y")] float y,
bool run = false)
{
// Apply the command to the game here.
}C# optional parameters become optional RPC arguments. Required arguments must precede optional arguments.
Supported types
Function parameters and return values must be one of the supported EL5 value types:
bool- all C# integer types
floatanddoublestringbyte[]DynamicParam[]for arraysIDictionary<string, DynamicParam>for maps
Flayer functions may return void, one of the supported values above, Task, Task<T>, or IEnumerator. For a coroutine that returns a value, set yieldReturnType on the attribute.
Returning arrays
Use DynamicParam[], not C# dynamic[] or object[]. Supported primitives are
converted automatically:
using Nunu.Flayer;
using Nunu.Network;
[FlayerFunction("print_board", "Prints the board as an array")]
public static DynamicParam[] HandlePrintBoard()
{
return new DynamicParam[]
{
"Test 1",
"Test 2"
};
}Error handling
A handler exception is returned to the caller as an EL5 Error frame, so the agent sees exactly what went wrong. When something can’t be done, throw — don’t return a false/null sentinel. Let unexpected failures propagate too instead of swallowing them.
[FlayerFunction("equip_item", "equips the item with the given id")]
public static void EquipItem(string itemId)
{
var item = Inventory.Find(itemId);
if (item == null)
throw new ArgumentException($"No item with id '{itemId}'");
Inventory.Equip(item);
}Key Features
- Automatic Registration: Functions are discovered and registered for every new connection.
- Thread Safety: Always executed on Unity’s main thread.
- Signature Validation: Ensures functions use supported parameter and return types.
- Volatile Support: Allows overriding default implementations.
- Error Handling: A handler exception is returned to the caller as an EL5
Errorframe. - Timeout Protection: Automatic timeout after 120 seconds.
Best Practices
- Always Validate Arguments: Check argument values before acting on them, and throw when they’re invalid.
- Throw on failure: An exception becomes an EL5 error the agent can read — don’t return a
bool/nulljust to signal that something failed. - Return
voidwhen there’s nothing to return: No need for a placeholderbool. - Log meaningful messages: Use
UnityEngine.Debug; logs are forwarded to the recording automatically. - Use Coroutines for Long Operations: Anything that takes multiple frames.
Examples
Simple Action (Single Frame)
[FlayerFunction("open_inventory", "opens the character's inventory")]
public static void HandleOpenInventory()
{
if (!inventory.CanOpen)
throw new InvalidOperationException("Cannot open inventory right now");
// Do short actions that complete in 1 frame
inventory.Open();
}Complex Action (Multiple Frames)
[FlayerFunction("move_to", "moves to the specified location", yieldReturnType: typeof(bool))]
public static IEnumerator HandleMoveTo(float x, float y, float z)
{
Vector3 targetPos = new Vector3(x, y, z);
Debug.Log($"Moving to position {targetPos}");
// Run multi-frame operations
yield return MovementSystem.NavigateTo(targetPos);
// Can yield to other coroutines
yield return WaitForAnimation();
// Return success
yield return true;
}Overriding a default function
The SDK ships fallback game_state and reset functions. Define a non-volatile function with the same name in your game to replace the default.
// Replace the default game_state with your own implementation
[FlayerFunction("game_state", "returns the actual game state", volatileFunction: false)]
public static string HandleGameState()
{
return JsonUtility.ToJson(new MyGameState());
}