---
title: Config Events
---

<Callout>
Added in Fzzy Config 0.5.0
</Callout>

Fzzy Config has an event system
* Methods in `Config` for managing events in your config [⤵](#-config-implementations)
* An `EventApi` for registering to generic config events [⤵](#-config-events)
* A way to attach listeners to [Validation](Validation) [⤵](#-listeners)

------------------------------------

## <div><SquareChartGanttIcon height={26} width={22} style={{display: "inline", color: "#B2BFF9", paddingTop: "4px", verticalAlign: "top"}} /> Config Implementations</div>
There are methods in `Config` that correspond to event stages. These are called alongside the events described below; if you only need to take action in your own config it's better to implement these than to register to the events, which are called for every config.

| Method           | Environment | Description                                                                                                                       |
|------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------|
| `onSyncClient`   | Client      | Called when a player logs in or data packs are reloaded                                                                           |
| `onSyncServer`   | Server      | Called when a player logs in or data packs are reloaded                                                                           | 
| `onUpdateClient` | Client      | Called after a config is updated in-game; when "apply changes" is used or the config GUI is closed, or when an update is received |
| `onUpdateServer` | Server      | Called when a update is synced to the server and it passes permission checks                                                      |

<Callout variant="info">
These methods are called on their respective main threads (Render/Server thread), so it is safe to interact with game state.
</Callout>

<CodeTabs>

```java !!tabs Java
public class MyConfig extends Config {
    
    /* constructor */

    //perform actions on update in a variety of contexts. 
    // Here you might be invalidating some cache because settings have changed, and then alerting the player.
    @Override
    public void onUpdateServer(ServerPlayerEntity playerEntity) {
        MyUtil.invalidateCacheFor(playerEntity);
        playerEntity.sendMessage("Settings updated, cache reset");
    }
    
}
```

```kotlin !!tabs Kotlin
class MyConfig: Config(Identifier.of(MODID, "my_config")) {
    
    //perform actions on update in a variety of contexts. 
    // Here you might be invalidating some cache because settings have changed, and then alerting the player.
    override fun onUpdateServer(playerEntity: ServerPlayerEntity) {
        MyUtil.invalidateCacheFor(playerEntity)
        playerEntity.sendMessage("Settings updated, cache reset")
    }
    
}
```

</CodeTabs>

## <div><CalendarSyncIcon height={26} width={22} style={{display: "inline", color: "#B2BFF9", paddingTop: "4px", verticalAlign: "top"}} /> Config Events</div>
The `EventApi` manages registration to events in Fzzy Config. Call the Event API with `ConfigApi.event()`. The events in the API mirror the methods in `Config`, but are fired for every config relevant to the event. They also pass generic information (`Config` vs. the exact config subclass).

| Method               | Environment       | Params                                                      | Description                                                                                                                      |
|----------------------|-------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `onSyncClient`       | Client            | config ID: Identifier, config: Config                       | Fired when a player logs in or data packs are reloaded                                                                           |
| `onSyncServer`       | Server            | config ID: Identifier, config: Config                       | Fired when a player logs in or data packs are reloaded                                                                           |
| `onUpdateClient`     | Client            | config ID: Identifier, config: Config                       | Fired after a config is updated in-game; when "apply changes" is used or the config GUI is closed, or when an update is received |
| `onUpdateServer`     | Server            | config ID: Identifier, config: Config, player: ServerPlayer | Fired when a update is synced to the server and it passes permission checks                                                      |
| `onRegisteredClient` | Client            | config ID: Identifier, config: Config                       | Fired when a config is registered on the client                                                                                  |
| `onRegisteredServer` | Client and Server | config ID: Identifier, config: Config                       | Fired when a config is registered on the _client and server_                                                                     |


<Callout variant="info">
Events are called on their respective main threads (Render/Server thread), so it is safe to interact with game state.
</Callout>

<CodeTabs>

```java !!tabs Java
public void init() {
    //register to events using the specified listener
    ConfigApi.event().onSyncClient((Identifier id, Config config) -> {
        //in this sync event we might wait until some config is synced to the client
        //and then run some init. This doesn't have to be your config! Use to time loading of integration too.
        if (id.equals(configICareAbout)) {
            MyUtils.initialize();
        }
    });

    ConfigApi.event().onRegisteredServer(configINeedToLoadAfter, (Config config) -> {
            //respect a load order, only load a config if another is present, do mod integration tasks once needed data from the other mod is ready, etc.
            MyIntegration.doStuff(config);
    });
}

```

```kotlin !!tabs Kotlin
fun init() {
    //register to events using the specified listener
    ConfigApi.event().onSyncClient { id: Identifier, config: Config ->
        //in this sync event we might wait until some config is synced to the client
        //and then run some init. This doesn't have to be your config! Use to time loading of integration too.
        if (id == configICareAbout) {
            MyUtils.initialize()
        }
    }
    
    ConfigApi.event().onRegisteredServer(configINeedToLoadAfter) { config: Config ->
        //respect a load order, only load a config if another is present, do mod integration tasks once needed data from the other mod is ready, etc.
        MyIntegration.doStuff(config)
    }
}

```

</CodeTabs>

## <div><MicIcon height={26} width={22} style={{display: "inline", color: "#B2BFF9", paddingTop: "4px", verticalAlign: "top"}} /> Listeners</div>
The above events are called when an update is "officially" completed for the entire config. If you need to inspect changes to a setting under any circumstance, either a player change or an in-code change, you can attach a listener to a `ValidatedField`.

These listeners are `Consumer` of the field itself. There are two methods available for attachment:
* `addListener`, directly in `ValidatedField` itself. This has no return; you will have to attach in an init block.
* `withListener`, an extension function with `ValidatedField` as a receiver. This method passes the field through, allowing you to attach inline, it also preserves the typing of the field.

<Callout variant="warning">
These changes may be made on any thread, depending on the particulars of how the field is interacted with. Take precautions when interacting with game state.
</Callout>

<CodeTabs>

```java !!tabs Java
// add listener is the direct approach to attaching a listener. It doesn't have a return nor does it preserve typing, so use of `withListener` is recommended
private static ValidatedBoolean helper() {
    ValidatedBoolean myBool = new ValidatedBoolean(false);
    myBool.addListener(myField -> do.stuff());
    return myBool;
}


ValidatedBoolean myListenedField = helper();

//with listener inlines the attachment more easily, and maintains the typing inside the consumer.
ValidatedBoolean myListenedBool = ValidatedField.withListener(new ValidatedBoolean(false), valBool -> do.stuff());
```

```kotlin !!tabs Kotlin
// add listener is the direct approach to attaching a listener. It doesn't have a return nor does it preserve typing, so use of `withListener` is recommended
var myListenedField = ValidatedBoolean(false).also { it.addListener { valField: ValidatedField<Boolean> -> do.stuff() } }

//with listener inlines the attachment more easily, and maintains the typing inside the consumer.
val myListenedBool = ValidatedBoolean(false).withListener { valBool: ValidatedBoolean -> do.stuff() }
```

</CodeTabs>