PUN Classic (v1), PUN 2 and Bolt are in maintenance mode. PUN 2 will support Unity 2019 to 2022, but no new features will be added. Of course all your PUN & Bolt projects will continue to work and run with the known performance in the future. For any upcoming or new projects: please switch to Photon Fusion or Quantum.
bolt | current

State Callbacks

Since Bolt 0.4+ one can define callbacks on arbitrary types. This has made the callback method a bit more complex, but allows for much more freedom on how you set up your callbacks.

state.AddCallback("myStateProperty", myCallbackFunction)


Note that triggers are a C# Delegate and don't require the state.AddCallback() method to be used. Instead you use adding to a delegate syntax. Because we can't have the name "MyTrigger" for both a property and a method, we use "OnMyTrigger" (or "OnX" for any event named "X") to register a state trigger callback. Like so:

state.OnMyTrigger += myCallbackFunction

Callbacks on arrays

You can set up a callback on an array in an array like so:

There is an overload to state.AddCallback which takes a delegate that takes three parameters in the format of (Bolt.IState, string path, Bolt.ArrayIndices indices).

  1. Bolt.IState can be cast to your state type within the method (note that Bolt.IState is the class all States inherit from).
    • This is your actual State though it's just not strongly typed (to have a generic AddCallback method); that's the reason you need to cast it to your own type.
  2. string path is the full path to the property.
  3. Bolt.ArrayIndices indices contains the indices for any of the arrays that you attached in the callback.

Example 1a: Array callback with method

state.AddCallback("stats[]", OnStatsChanged);
public void OnStatsChanged(Bolt.IState state, string path, Bolt.ArrayIndices indices)
        int index = indices[0];
        IActorState actorState = (IActorState)state;
        // The changed property:
        // actorState.stats[index]

Example 1b: Array callback with lambda

state.AddCallback("stats[]", (x, y, z) => { OnStatsChanged((IActorState)x, (string)y, (Bolt.ArrayIndices)z); });
public void OnStatsChanged(IActorState state, string path, Bolt.ArrayIndices indices)
        int index = indices[0];

Example 2a: Nested array with method

state.AddCallback("vaults[].contents[]",  OnVaultContentsChanged);
public void OnVaultContentsChanged(Bolt.IState state, string path, Bolt.ArrayIndices indices)
        IActorState actorState = (IActorState)state;
        int vaultIndex = indices[0];
        int index = indices[1];
        int itemID = actorState.vaults[vaultIndex].contents[index];

Example 2b: Nested array callback with lambda

state.AddCallback("vaults[].contents[]", (x, y, z) => { OnVaultContentsChanged((IActorState)x, (string)y, (Bolt.ArrayIndices)z); });
public void OnVaultContentsChanged(IActorState state, string path, Bolt.ArrayIndices indices)
        int vaultIndex = indices[0];
        int index = indices[1];
        int itemID = state.vaults[vaultIndex].contents[index];

Example 3: Using a look-up table for methods in your struct callback

If you have defined a struct in your State and you want a single global callback instead of setting a callback for each of its fields and you still want a separate method to be called per specific property then it's recommended to use a look-up table as opposed to a switch statement.
This is more of an optimization than a necessity, also this will only be of any benefit if the callback is triggered a lot and there are many fields in your struct.

Defining your look-up table of Actions:

Dictionary<string, Action> lookupTable = new Dictionary<string, Action>()
    { "equippedItems.head", UpdateArmor(state.equippedItems.head, 0) },
    { "equippedItems.body", UpdateArmor(state.equippedItems.body, 1) },
    { "equippedItems.arms", UpdateArmor(state.equippedItems.arms, 2) }

Action UpdateArmor(Item item, int index) {
  return () => UpdateUMAArmor(item, index);

Note that you can't access the State's parameters until it has been attached. Therefore you should fill up the look-up table within Attached() OR do a look-up table that is initialized with lazy access.
Otherwise you might end up with the following error:

Error: An object reference is required for the non-static field, method, or property 'Bolt.EntityBehaviour<IPlayerState>.state.get'

And this would be used to add a callback:

state.AddCallback("equippedItems", UpdateNewArmor);

And this is the method:

public void UpdateNewArmor(Bolt.IState state, string path, Bolt.ArrayIndices indices)

To Document Top